1、开始线程的几种方式
2、Task.Delay 线程延迟执行
3、线程池
(1、Task.WaitAny :等待某一个线程执行完毕以后 继续往后执行
(2、Task.WaitAll:等待所有线程执行完,后执行。
4、Parallel
5、多线程异常处理
6、线程取消
7、多线程中的临时变量
8、线程安全 lock 锁
9、线程属性 如:线程ID
开始线程的几种方式
----------------------1---------------------------
Task task = new Task(() =>
{
this.DoSomethingLong("btntask_Click1");
});
task.Start();//开启了一个新的线程
//-------------------------2----------------------------
Task.Run(() =>
{
this.DoSomethingLong("btntask_Click2");
});
有返回值的--------------------3--Result------------------------
Task<int> result = Task.Run<int>(() =>
{
Thread.Sleep(2000);
return DateTime.Now.Year;
});
int iResult = result.Result; //会卡界面等待
Task.Run<int>(() =>
{
Thread.Sleep(2000);
return DateTime.Now.Year;
}).ContinueWith(intT => //开启一个延续任务
{
int i = intT.Result;
});
3 线程池
TaskFactory taskFactory = Task.Factory;
taskFactory.StartNew(() =>
{
this.DoSomethingLong("btntask_Click3");
});
Task.Delay 线程延迟执行
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Task task = Task.Delay(2000).ContinueWith(t => //任务在2000ms 以后执行
{
stopwatch.Stop();
Console.WriteLine($"{stopwatch.ElapsedMilliseconds}");
Console.WriteLine("回调已完成");
});
线程池TaskFactory ()
1、卡界面
Task.WaitAny :等待某一个线程执行完毕以后 继续往后执行
2、Task.WaitAll:等待所有线程执行完,后执行。
不卡界面,回调实现
ContinueWhenAny:等待某一个线程执行完毕以后 继续往后执行
ContinueWhenAll:等待所有线程执行完,后执行。
List<Task> tasksList = new List<Task>();
TaskFactory taskFactory = new TaskFactory();
tasksList.Add(taskFactory.StartNew(() => { this.Coding("小米", "系统管理"); }));
tasksList.Add(taskFactory.StartNew(() => { this.Coding("小孩", "部门管理"); }));
tasksList.Add(taskFactory.StartNew(() => { this.Coding("小吕", "客户管理"); }));
tasksList.Add(taskFactory.StartNew(() => { this.Coding("小组", "接口管理"); }));
tasksList.Add(taskFactory.StartNew(() => { this.Coding("撒的", "写Api"); }));
//可以不卡界面,提高性能
// 有没有既不卡界面,也能在所有人完成以后,做点其他别的事儿;
//这就是回调的实现
// 在这一堆任务中,只有有一个执行完毕就执行指定的任务,
taskFactory.ContinueWhenAny(tasksList.ToArray(), t => { Console.WriteLine($"只要有一个人完成,即可触发,获取一个红包奖励!"); });
taskFactory.ContinueWhenAll(tasksList.ToArray(), t => { Console.WriteLine($"等待所以只需完毕, 所有人任务开发完毕以后,大家一起庆祝一下!"); });
//如果有一个同学完成了某一个模块,老师就需要准备环境!
// 等待某一个线程执行完毕以后 继续往后执行
Task.WaitAny(tasksList.ToArray());
Console.WriteLine("老师开始准备环境部署项目!");
//老师要等待大家都完成了以后,开始给点评!
Task.WaitAll(tasksList.ToArray()); //阻塞主线程
Console.WriteLine("5个模块均已完成,老师点评!");
// ContinueWhenAny 带传参,回调。
List<Task> tasksList = new List<Task>();
TaskFactory taskFactory = new TaskFactory();
tasksList.Add(taskFactory.StartNew((o) => { this.Coding("流光易逝", "部门管理"); }, "流光易逝"));
tasksList.Add(taskFactory.StartNew((o) => { this.Coding("偏执", "客户管理"); }, "偏执"));
tasksList.Add(taskFactory.StartNew((o) => { this.Coding("清茶", "接口管理"); }, "清茶"));
tasksList.Add(taskFactory.StartNew((o) => { this.Coding("秋陌", "写Api"); }, "秋陌"));
taskFactory.ContinueWhenAny(tasksList.ToArray(), t => { Console.WriteLine($" {Thread.CurrentThread.ManagedThreadId.ToString("00")} {t.AsyncState}获取一个红包奖励!"); });
//所调用方法
private void Coding(string name, string projectName)
{
Console.WriteLine($"****************{name}Coding Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ***************");
int iResult = 0;
for (int i = 0; i < 100_000_000; i++)
{
iResult += i;
}
Console.WriteLine($"****************{name} Coding End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ***************");
}
Parallel 对Task进一步进行了封装 .Netframework 4.5版本出来
优点:使用Parallel可以控制线程的执行顺序。
/// <summary>
/// Parallel 对Task进一步进行了封装 .Netframework 4.5版本出来
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnParallel_Click(object sender, EventArgs e)
{
Console.WriteLine($"**************** btnParallel_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ***************");
{
//多线程中控制执行顺序,一直是一个难题,使用Parallel可以控制线程的执行顺序。
//Parallel并发执行了五个委托,开启了新线程,主线程参与计算,界面会阻塞
// Task WaitAll + 主线程
Parallel.Invoke(() => { this.DoSomethingLong("btnParallel_Click_1"); },
() => { this.DoSomethingLong("btnParallel_Click_2"); },
() => { this.DoSomethingLong("btnParallel_Click_3"); },
() => { this.DoSomethingLong("btnParallel_Click_4"); },
() => { this.DoSomethingLong("btnParallel_Click_5"); });
}
{
ParallelOptions parallelOptions = new ParallelOptions();
parallelOptions.MaxDegreeOfParallelism = 2;
Parallel.For(0, 10, parallelOptions,t => this.DoSomethingLong($"btnParallel_Click_{t}"));
}
{
控制线程数量
ParallelOptions parallelOptions = new ParallelOptions();
parallelOptions.MaxDegreeOfParallelism = 2; //控制线程的最大数量
//控制执行数量
Parallel.ForEach(new int[] { 12, 13, 14, 15, 16, 17 }, parallelOptions, t => this.DoSomethingLong($"btnParallel_Click_{t}"));
}
Console.WriteLine($"**************** btnParallel_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ***************");
}
多线程异常处理
//Task.WaitAll(taskList.ToArray()); WaitAll 之后才能捕捉到所有的异常信息
#region 多线程异常处理
{
try
{
List<Task> taskList = new List<Task>();
for (int i = 0; i < 50; i++)
{
string name = $"btnThreadCore_Click_{i}";
int k = i;
taskList.Add(Task.Run(() =>
{
//try
//{
if (k == 5)
{
throw new Exception($"{name} {Thread.CurrentThread.ManagedThreadId.ToString("00")} 异常了 ");
}
else if (k == 6)
{
throw new Exception($"{name} {Thread.CurrentThread.ManagedThreadId.ToString("00")} 异常了");
}
else if (k == 10)
{
throw new Exception($"{name} {Thread.CurrentThread.ManagedThreadId.ToString("00")} 异常了");
}
Console.WriteLine($"this is {name} {Thread.CurrentThread.ManagedThreadId.ToString("00")} Ok!");
//}
//catch (Exception)
//{
// Console.WriteLine("异常");
//}
}));
};
//Task.WaitAll(taskList.ToArray()); //如果这里不等待 try -catch 能否捕捉到异常 ,不能
//只有WaitAll 之后才能捕捉到所有的异常信息
//在实际开发中是不允许在子线程中出现异常的
}
catch (AggregateException aex) //可以有多个Catch 在匹配异常类型的时候,先具体,然后在寻找父类
{
foreach (var exception in aex.InnerExceptions)
{
Console.WriteLine(exception.Message);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw;
}
//可以通过等待线程完成来捕捉异常!
//工作中的建议:多线程的委托中,不允许出现异常,包一层try-catch,然后记录下相关信息;
}
#endregion
线程取消
CancellationTokenSource
#region 线程取消
{
//在我们实际的开发工作中,在使用多线程的时候,往往有一个线程出现异常之后,就需要停止其他的线程;
// 在Task中开启的线程是无法从外部取消,只能自己停止自己
//{
// Thread thread;
// thread.Abort(); //停止当前线程
//}
//全局变量
//bool IsOK = true;
CancellationTokenSource cts = new CancellationTokenSource(); //线程安全
//cts 有个状态值 IsCancellationRequested 初始化位false
//提供一个Cancel() 方法改变IsCancellationRequested状态(不能修改)
try
{
List<Task> taskList = new List<Task>();
for (int i = 0; i < 50; i++)
{
string name = $"btnThreadCore_Click_{i}";
int k = i;
Thread.Sleep(new Random().Next(50, 100)); //休息五到十秒
taskList.Add(Task.Run(() =>
{
try
{
if (!cts.IsCancellationRequested)
{
Console.WriteLine($" his is {name} 开始执行 {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
}
if (k == 5)
{
///.....
///如果这里就需要种植其他线程 这里就直接Cancel
//cts.Cancel();
//IsOK = false;
throw new Exception($"{name} {Thread.CurrentThread.ManagedThreadId.ToString("00")} 异常了 ");
}
else if (k == 6)
{
throw new Exception($"{name} {Thread.CurrentThread.ManagedThreadId.ToString("00")} 异常了");
}
if (!cts.IsCancellationRequested)
{
Console.WriteLine($"this is {name} 执行结束 {Thread.CurrentThread.ManagedThreadId.ToString("00")} Ok!");
}
else
{
Console.WriteLine($"this is {name} 取消了 {Thread.CurrentThread.ManagedThreadId.ToString("00")} Ok!");
}
}
catch (Exception ex)
{
cts.Cancel();// 可以根据自己的业务需求直接Cancel
Console.WriteLine(ex.Message);
}
// 1 创建cts 共享变量 2 try-catch 捕捉异常 3 在开启的线程中 判断Action
// 2 那么如果有异常出现以后,能不能让未开启的线程就直接不开启了呢?
//Task Run(Action action, CancellationToken cancellationToken)
//cts.Token.ThrowIfCancellationRequested
//1 启动线程传递token 2 异常捕捉
}, cts.Token));
};
Task.WaitAll(taskList.ToArray());
}
catch (AggregateException aex)
{
foreach (var exception in aex.InnerExceptions)
{
Console.WriteLine(exception.Message);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
#endregion
多线程中的临时变量
执行发现i==20
k 只是针对于某一次for 循环,循环20次就会有20 k,所以在次打印即为正常循环次数
#region 多线程中的临时变量
{
for (int i = 0; i < 20; i++) //for 循环很快
{
Task.Run(() => // 开启线程;不会阻塞的,线程会延迟启动/执行发现i==20
{
Console.WriteLine($"btnThreadCore_Click_{i} 线程Id={Thread.CurrentThread.ManagedThreadId.ToString("00")}");
});
}
for (int i = 0; i < 20; i++)
{
int k = i; //作用域 这个k 是不是只是针对于某一次for 循环,循环20次就会有20 k
Task.Run(() =>
{
Console.WriteLine($"btnThreadCore_Click_{k} 线程Id={Thread.CurrentThread.ManagedThreadId.ToString("00")}");
});
}
}
#endregion
线程安全
lock 锁
#region 线程安全
//线程安全:如果你的代码用多线程运行和单线程运行的结果相同;
//线程不安全
//线程不安全一般出现在全局变量/共享变量/磁盘文件/静态变量/数据库的值/只要是多项去访问修改的时候,就可能会出现线程安全;
//磁盘文件:如果开启多个线程去操作文件,A---操作 B--也操作
//两次执行不一样:100000, 99934
//1 Lock;不能锁null,不建议大家锁String; 锁 this
//使用锁:private static readonly object Obj_Lock = new object()
//锁的作用:排他
//2 线程安全集合
// System.Collections.Concurrent.ConcurrentStack 基于线程安全
//3 数据分拆,避免多个线程操作同一堆数据,安全又高效率
for (int i = 0; i < 100000; i++)
{
this.NumOne += 1;
}
for (int i = 0; i < 100000; i++)
{
Task.Run(() =>
{
lock (Obj_Lock)//可以 避免多线程并发,如果锁住以后,其实这里跟单线程基本上没啥区别;
{
this.NumTow += 1;
}
});
}
//我们的职责是如何解决线程安全问题:
//Lock:锁,可以锁住一个引用,避免多个线程同时并发:
Console.WriteLine($"**************** btnThreadCore_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ***************");
#endregion
/// <summary>
/// 强烈建议大家使用Object类型
/// </summary>
private static readonly object Obj_Lock = new object();
线程属性 如:线程ID
/// <summary>
/// Task开启线程
/// </summary>
public void Tasks()
{
for (int i = 0; i <= 20; i++)
{
Task.Run(() =>
{
Console.WriteLine("11111111" + DateTime.Now + "线程ID" + Thread.CurrentThread.ManagedThreadId.ToString("00"));
Console.WriteLine("是否背地里跑线程" + Thread.CurrentThread.IsBackground);
int WORKERtHREAD = 0;
int ioThread = 0;
ThreadPool.GetMinThreads(out WORKERtHREAD, out ioThread);
Console.WriteLine("最大线程:" + WORKERtHREAD + " 最大OI线程:" + ioThread);
int WORKERtHREADs = 0;
int ioThreads = 0;
ThreadPool.GetAvailableThreads(out WORKERtHREADs, out ioThreads);
Console.WriteLine("可用线程:" + WORKERtHREADs + " 可用IO线程:" + ioThreads);
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine(1 + "线程ID" + Thread.CurrentThread.ManagedThreadId.ToString("00"));
Task.Delay(10000).Wait();
Console.WriteLine(2);
});
}
}