C#/.NET 多线程任务Task的详解——应用实例
Task类介绍:
Task 类的表示单个操作不返回一个值,通常以异步方式执行。Task 对象是一个的中心思想 基于任务的异步模式 首次引入.NET Framework 4 中。因为由执行工作 Task 对象通常以异步方式执行在线程池线程上而不是以同步方式在主应用程序线程,可以使用 Status 属性,以及 IsCanceled, ,IsCompleted, ,和 IsFaulted 属性,以确定任务的状态。
任务Task和线程Thread的区别:
1、任务是架构在线程之上的,也就是说任务最终还是要抛给线程去执行。
2、任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线程池,但是任务相比线程池有很小的开销和精确的控制。
Task和Thread一样,位于System.Threading命名空间下
01
Task的多种启动方式
先定义一个耗时间耗资源的方法
/// <summary>
/// 一个比较耗时耗资源的私有方法
/// </summary>
/// <param name="name"></param>
private void DoSomethingLong(string name)
{
Console.WriteLine($"****************DoSomethingLong {name} Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
long lResult = 0;
for (int i = 0; i < 1000000000; i++)
{
lResult += i;
}
//Thread.Sleep(2000);
Console.WriteLine($"****************DoSomethingLong {name} End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {lResult}***************");
}
三种常用的启动方式
/// <summary>
/// 多种线程启动方式
/// </summary>
private void StartThread()
{
Task.Run(() => this.DoSomethingLong("btnTask_Click1"));
Task.Factory.StartNew(() => this.DoSomethingLong("btnTask_Click2"));
new Task(() => this.DoSomethingLong("btnTask_Click3")).Start();
//启动带回调
Task.Run(() => this.DoSomethingLong("Client")).ContinueWith(t => { });
}
02
同步延时及异步延时
/// <summary>
/// 延时操作
/// </summary>
private void Delay()
{
//Task.Delay(1000);//延迟 不会卡
//Thread.Sleep(1000);//等待 卡
#region 同步延时,卡界面
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Thread.Sleep(2000);
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds);
#endregion
#region 异步延时,不卡界面
Stopwatch stopwatch2 = new Stopwatch();
stopwatch.Start();
Task.Delay(2000).ContinueWith(t =>
{
stopwatch.Stop();
Console.WriteLine(stopwatch2.ElapsedMilliseconds);
});
#endregion
#region 同步+异步延时,不卡界面
Stopwatch stopwatch3 = new Stopwatch();
stopwatch.Start();
Task.Run(() =>
{
Thread.Sleep(2000);
stopwatch.Stop();
Console.WriteLine(stopwatch3.ElapsedMilliseconds);
});
#endregion
}
03
通过判断线程状态来控制线程最大运行数
/// <summary>
/// 限制多线程开启数量
/// </summary>
private void LimitThreadCount()
{
var maxCount = 10;
List<int> list = new List<int>();
for (int i = 0; i < 10000; i++)
{
list.Add(i);
}
Action<int> action = i =>
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString("00"));
Thread.Sleep(new Random(i).Next(100, 300));
};
List<Task> taskList = new List<Task>();
foreach (var i in list)
{
int k = i;
taskList.Add(Task.Run(() => action.Invoke(k)));
if (taskList.Count > maxCount)
{
Task.WaitAny(taskList.ToArray());
taskList = taskList.Where(t => t.Status != TaskStatus.RanToCompletion).ToList();
}
}
//异步等待其全部执行完毕,不阻塞线程
Task wTask = Task.WhenAll(taskList.ToArray());
//wTask.ContinueWith()...
//死等线程全部执行完毕,阻塞后面的线程
Task.WaitAll(taskList.ToArray());
}
04
通过信号量来控制最大线程运行数
需先参考封装MutipleThreadResetEvent类:
/// <summary>
/// 通过信号量来控制最大线程运行数
/// </summary>
private void LimitThreadCountBySemaphore()
{
CancellationTokenSource cts = new CancellationTokenSource();//线程取消
List<int> lsInt = new List<int>() { 1, 2, 3, 4, 5, 6 };
int num = lsInt.Count;
var maxThread = num > 5 ? 5 : num;
Semaphore semaphore = new Semaphore(maxThread, maxThread);
MutipleThreadResetEvent mutipleThreadReset = new MutipleThreadResetEvent(num);
foreach (var valueItem in lsInt)
{
semaphore.WaitOne();//信号量控制最大运行线程
Task.Factory.StartNew((obj) =>
{
try
{
if (!cts.IsCancellationRequested)
{
//...
}
}
catch (ThreadAbortException) { cts.Cancel(); }
finally
{
semaphore.Release();
mutipleThreadReset.SetOne();
}
}, valueItem, cts.Token);
}
mutipleThreadReset.WaitAll();
}
- THE END -
🍁