C#多线去对数据库进行添加操作,ManualResetEvent报The number of WaitHandles must be less than or equal to 64解决方案

26 篇文章 9 订阅
4 篇文章 0 订阅

今天做项目有个需求,就是添加添加10万条数据到数据库,或者更多,然后为了能提高效率,很当然的想到了多线程操作。

问题

但是,我们通过foreach去开辟线程的时候,该如何去判断所有线程都执行完了呢?

解决:

这里网上找了些资料最后决定用ManualResetEvent这个类去实现对线程的控制,大概内容就是通过这个类 给每个独立的线程添加一个信号量,可以添加到线程代码的最后,通过Set()去设置,如果被标记,就代表这个线程执行完毕了。具体ManualResetEvent用法可以参考这篇文章ManualResetEvent

实现代码

 public async Task Copey(Dictionary<string, string> ids)
    {
        await Task.Run(()=> {

            ManualResetEvent[] _ManualEvents = new ManualResetEvent[boxids.Count];
            int i = 0;
            foreach (var item in boxids)
            {
                try
                {
                    _ManualEvents[i] = new ManualResetEvent(false);
                    new Thread(async (x) =>
                    {
                       //核心业务代码 写在这里(。。。。。)
                       //核心代码执行完毕 ,就设置一下信号量
                        //给当前线程设置个信号量
                        ManualResetEvent e = (ManualResetEvent)x;
                        e.Set();
                    }).Start(_ManualEvents[i]);
                }
                catch (Exception e)
                {

                    throw new Exception($"current Tread is {Thread.CurrentThread.ManagedThreadId}err:{e.Message}");
                }
                finally
                {
                    i++;
                }

                Thread.Sleep(50);
            }
            //等待所有线程执行完毕
            var flage = WaitHandle.WaitAll(_ManualEvents);
            if (flage)
            {
                System.Console.WriteLine("执行完成!");
            }

        });


    }

但是这样浅看一切正常,但是ManualResetEvent[] _ManualEvents = new ManualResetEvent[ids.Count];ids.Count大于64的时候,就抛异常,The number of WaitHandles must be less than or equal to 64这句话意思就是说,WaitHandle.WaitAll(_ManualEvents);这个对象等待的最大线程数量是64。

The number of WaitHandles must be less than or equal to 64 解决方案

我们居然知道了如何去判断所有线程是否执行完毕,那不如我们自己封装一个类。代码如下:

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

namespace SampleBank.Shared.Shared
{
    public class MutipleThreadResetEvent : IDisposable
    {

        private readonly ManualResetEvent done;
        private readonly int total;
        private long current;
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="total">需要等待执行的线程总数</param>
        public MutipleThreadResetEvent(int total)
        {
            this.total = total;
            current = total;
            done = new ManualResetEvent(false);
        }

        /// <summary>
        /// 唤醒一个等待的线程
        /// </summary>
        public void SetOne()
        {
            // Interlocked 原子操作类 ,此处将计数器减1
            if (Interlocked.Decrement(ref current) == 0)
            {
                //当所以等待线程执行完毕时,唤醒等待的线程
                done.Set();
            }
        }

        /// <summary>
        /// 等待所有线程执行完毕
        /// </summary>
        public void WaitAll()
        {
            done.WaitOne();
        }

        /// <summary>
        /// 释放对象占用的空间
        /// </summary>
        public void Dispose()
        {
            ((IDisposable)done).Dispose();
        }
    }
}

用法:

我们把线程数量通过构造函数的方式给到类中的变量,然后每次创建一个线程的时候就调用一下SetOne这个函数,然后当前线程数值current减一,当等同于0的时候,WaitAll等待结束。

具体代码如下:

  public async Task Copey(Dictionary<string, string> boxids)
    {
        await Task.Run(()=> {
            using (MutipleThreadResetEvent mutipleThreadResetEvent = new MutipleThreadResetEvent(boxids.Count))
            {
                foreach (var item in boxids)
                {
                    try
                    {

                        new Thread(async (x) =>
                        {
                            //核心业务代码(。。。。。)
                            //给当前线程设置个信号量
                            MutipleThreadResetEvent e = (MutipleThreadResetEvent)x;
                            e.SetOne();
                        }).Start(mutipleThreadResetEvent);
                    }
                    catch (Exception e)
                    {

                        throw new Exception($"current Tread is {Thread.CurrentThread.ManagedThreadId}err:{e.Message}");
                    }
                

                
                }

                //等待所有线程执行完毕 再执行后面代码
                mutipleThreadResetEvent.WaitAll();
              
            }





        });


    }

这样就可以很好的解决64的问题,而且只需要用到一个类。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值