今天做项目有个需求,就是添加添加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的问题,而且只需要用到一个类。