C#多线程处理List集合数据

List集合是非线程安全的,所以我们采用并行编程时会发生错误。如下图所示

 Parallel.For(0, 1000, (i) =>
      {
        Product product = new Product();
        product.Name = "name" + i;
        product.Category = "Category" + i;
        product.SellPrice = i;
        _Products.Add(product);
      });

代码中开启并发操作向集合中添加1000条数据,在没有保障线程安全和串行化的运行下,实际得到的数据不到1000条。为此我们需要采用Lock关键字,来确保每次只有一个线程来访问 ,可以得到1000条数据。

private static object o = new object();
Parallel.For(0, 1000, (i) =>
      {
        Product product = new Product();
        product.Name = "name" + i;
        product.Category = "Category" + i;
        product.SellPrice = i;
        lock (o)
        {
          _Products.Add(product);
        }
      });

但是锁的引入,带来了一定的开销和性能的损耗,并降低了程序的扩展性,在并发编程中显然不适用。为此,在.NET Framework 4中提供了System.Collections.Concurrent新的命名空间可以访问用于解决线程安全问题,它们能够解决潜在的死锁问题和竞争条件问题,因此在很多复杂的情形下它们能够使得并行代码更容易编写,这些集合尽 可能减少需要使用锁的次数,从而使得在大部分情形下能够优化为最佳性能,不会产生不必要的同步开销。

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

通过这个命名空间能访问以下为并发做好了准备的集合。

1.BlockingCollection 与经典的阻塞队列数据结构类似,能够适用于多个任务添加和删除数据,提供阻塞和限界能力。

2.ConcurrentBag 提供对象的线程安全的无序集合

3.ConcurrentDictionary  提供可有多个线程同时访问的键值对的线程安全集合

4.ConcurrentQueue   提供线程安全的先进先出集合

5.ConcurrentStack   提供线程安全的后进先出集合

这些集合通过使用比较并交换和内存屏障等技术,避免使用典型的互斥重量级的锁,从而保证线程安全和性能。

      _ConcurrenProducts = new ConcurrentQueue<Product>();
      /*创建任务 tk1  tk1 执行 数据集合添加操作*/
      Task tk1 = Task.Factory.StartNew(() =>
      {
        AddConcurrenProducts();
      });
    Task.WaitAll(tk1);
/*执行集合数据添加操作*/
    static void AddConcurrenProducts()
    {
      Parallel.For(0, 30000, (i) =>
      {
        Product product = new Product();
        product.Name = "name" + i;
        product.Category = "Category" + i;
        product.SellPrice = i;
        _ConcurrenProducts.Enqueue(product);
      });

    }

在处理的List集合数据时,可将其放到ConcurrentQueue中,然后开启多个线程去处理数据,处理完成后,再到队列中获取下一个待处理数据。

/// <summary>
/// 多线程处理数据(无返回值)
/// </summary>
/// <typeparam name="T">数据类型</typeparam>
/// <param name="list">待处理数据</param>
/// <param name="action">数据处理方法(有参数无返回值)</param>
/// <param name="count">处理线程数量</param>
/// <param name="waitFlag">是否等待执行结束</param>
static void RunTask<T>(List<T> list, Action<T> action, int threadCount = 5, bool waitFlag = true)
{
  ConcurrentQueue<T> queue = new ConcurrentQueue<T>(list);
  Task[] tasks = new Task[threadCount];
  for (int i = 0; i < threadCount; i++)
  {
     tasks[i] = Task.Factory.StartNew(() =>
     {
       while (queue.TryDequeue(out T t))
       {
         action(t);
       }
     });
  }
  if (waitFlag)
  {
    Task.WaitAll(tasks);
  }
}

/// <summary>
/// 多线程处理数据(返回处理后列表)
/// </summary>
/// <typeparam name="T">数据类型</typeparam>
/// <param name="list">待处理数据</param>
/// <param name="func">数据处理方法(有参数有返回值)</param>
/// <param name="threadCount">处理线程数量</param>
/// <returns>数据处理后结果</returns>
static List<T> RunTask<T>(List<T> list, Func<T, T> func, int threadCount = 5)
{
  var result = new List<T>();
  ConcurrentQueue<T> queue = new ConcurrentQueue<T>(list);
  Task<List<T>>[] tasks = new Task<List<T>>[threadCount];
  for (int i = 0; i < threadCount; i++)
  {
    tasks[i] = Task.Factory.StartNew<List<T>>(() =>
    {
      var rList = new List<T>();
      while (queue.TryDequeue(out T t))
      {
        rList.Add(func(t));
      }
      return rList;
    });
  }
  Task.WaitAll(tasks);
  for (int i = 0; i < threadCount; i++)
  {
    result.AddRange(tasks[i].Result);
  }
  return result;
}

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值