ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

多线程----并发集合

2022-07-03 16:34:16  阅读:179  来源: 互联网

标签:Task students t2 System ---- 并发 student 集合 多线程


       在C#语言中当需要处理并发的场景时,就需要程序员使用最合理的数据结构。那么哪些数据结构是支持和可以在并行计算中被使用的呢。首先这些数据结构具备可伸缩性,尽可能地避免锁(会造成多个线程的等待,防止资源竞争),同时还能提供线程安全的访问。

在.NET Framework4.0中引入了System.Collections.Concurrent命名空间,其中就包含几个数据结构。

  • ConcurrentQueue:提供线程安全的先进先出集合
  • ConcurrentDictionary:提供可有多个线程同时访问的键值对的线程安全集合
  • ConcurrentStack:提供线程安全的后进先出集合
  • ConcurrentBag:提供对象的线程安全的无序集合
  • BlockingCollect:与经典的阻塞队列数据结构类似,能够适用于多个任务添加和删除数据,提供阻塞和限界能力。

.NET Framework 4提供了新的线程安全和扩展的并发集合,它们能够解决潜在的死锁问题和竞争条件问题,因此在很多复杂的情形下它们能够使得并行代码更容易编写,这些集合尽可能减少需要使用锁的次数,从而使得在大部分情形下能够优化为最佳性能,不会产生不必要的同步开销。

需要注意的是:

线程安全并不是没有代价的,比起System.Collenctions和System.Collenctions.Generic命名空间中的列表、集合和数组来说,并发集合会有更大的开销。因此,应该只在需要从多个任务中并发访问集合的时候才使用并发几个,在串行代码中使用并发集合是没有意义的,因为它们会增加无谓的开销。

 

为此,在.NET Framework中提供了System.Collections.Concurrent新的命名空间可以访问用于解决线程安全问题,通过这个命名空间能访问以下为并发做好了准备的集合。

下面看代码,代码中并没有实现线程安全和串行化:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace ConcurrentDemo
{
    class Program
    {
        private static List<Student> students;
        static void Main(string[] args)
        {
            students = new List<Student>();
            /*创建任务 t1  t1 执行 数据集合添加操作*/
            Task t1 = Task.Factory.StartNew(() =>
            {
                AddStudent();
            });
            /*创建任务 t2  t2 执行 数据集合添加操作*/
            Task t2 = Task.Factory.StartNew(() =>
            {
                AddStudent();
            });
            /*创建任务 t3  t3 执行 数据集合添加操作*/
            Task t3 = Task.Factory.StartNew(() =>
            {
                AddStudent();
            });
            Task.WaitAll(t1, t2, t3);
            Console.WriteLine(students.Count);
            Console.ReadLine();
        }

        static void AddStudent()
        {
            Parallel.For(0, 1000, (i) =>
            {
                Student student = new Student();
                student.Id = i;
                student.Name = "name" + i;
                students.Add(student);
            });
        }
    }

    class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
}

代码中开启了三个并发操作,每个操作都向集合中添加1000条数据,在没有保障线程安全和串行化的运行下,实际得到的数据并没有3000条,结果如下:

 

 为此我们需要采用Lock关键字,来确保每次只有一个线程来访问  students.Add(student); 这个方法,代码如下:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace ConcurrentDemo
{
    class Program
    {
        private static object obj = new object();
        private static List<Student> students;
        static void Main(string[] args)
        {
            students = new List<Student>();
            /*创建任务 t1  t1 执行 数据集合添加操作*/
            Task t1 = Task.Factory.StartNew(() =>
            {
                AddStudent();
            });
            /*创建任务 t2  t2 执行 数据集合添加操作*/
            Task t2 = Task.Factory.StartNew(() =>
            {
                AddStudent();
            });
            /*创建任务 t3  t3 执行 数据集合添加操作*/
            Task t3 = Task.Factory.StartNew(() =>
            {
                AddStudent();
            });
            Task.WaitAll(t1, t2, t3);
            Console.WriteLine(students.Count);
            Console.ReadLine();
        }

        static void AddStudent()
        {
            Parallel.For(0, 1000, (i) =>
            {
                Student student = new Student();
                student.Id = i;
                student.Name = "name" + i;
                lock (obj)
                { 
                  students.Add(student);
                }
            });
        }
    }

    class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
}

 

 但是锁的引入,带来了一定的开销和性能的损耗,并降低了程序的扩展性,在并发编程中显然不适用。

 

上述例子改成使用ConcurrentBag类型:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace ConcurrentDemo
{
    class Program
    {
        //private static object obj = new object();
        private static ConcurrentBag<Student> students;
        static void Main(string[] args)
        {
            students = new ConcurrentBag<Student>();
            /*创建任务 t1  t1 执行 数据集合添加操作*/
            Task t1 = Task.Factory.StartNew(() =>
            {
                AddStudent();
            });
            /*创建任务 t2  t2 执行 数据集合添加操作*/
            Task t2 = Task.Factory.StartNew(() =>
            {
                AddStudent();
            });
            /*创建任务 t3  t3 执行 数据集合添加操作*/
            Task t3 = Task.Factory.StartNew(() =>
            {
                AddStudent();
            });
            Task.WaitAll(t1, t2, t3);
            Console.WriteLine(students.Count);
            Console.ReadLine();
        }

        static void AddStudent()
        {
            Parallel.For(0, 1000, (i) =>
            {
                Student student = new Student();
                student.Id = i;
                student.Name = "name" + i;
                students.Add(student);
            });
        }
    }

    class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
}

返回结果也是3000

具体每种集合的API可以查看 https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.concurrent?redirectedfrom=MSDN&view=net-6.0 

 

本文参考链接:https://www.cnblogs.com/woxpp/p/3935557.html

 

标签:Task,students,t2,System,----,并发,student,集合,多线程
来源: https://www.cnblogs.com/hobelee/p/16440075.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有