using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Threading.Tasks;
namespace threadTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btntongbu_Click(object sender, EventArgs e)
{
this.AddContentTongbu($"btntongbu_Click 开始 {Thread.CurrentThread.ManagedThreadId}");
Action<string> action = this.AddContentTongbu;
for (int i = 0; i < 5000; i++)
{
action.Invoke($"btntongbu_Click_{i},ManagedThreadId= {Thread.CurrentThread.ManagedThreadId}");
}
this.AddContentTongbu($"btntongbu_Click 结束 {Thread.CurrentThread.ManagedThreadId}");
}
private void AddContentTongbu(string content)
{
this.textBox1.AppendText(content + "\r\n");
}
private void btnClearText_Click(object sender, EventArgs e)
{
this.textBox1.Clear();
}
private void DoSomeThingLongYIBU(string content)
{
//3.Control的Invoke和BeginInvoke 是相对于分线程(一般在分线程中调用,用来更新主线程ui)
//Invoke立即插入主线程中执行(大致理解invoke表是同步);
// 而BeginInvoke 表示异步,它是相对于调用线程的异步,而不是相对于UI线程的异步。
//两者的区别就是一个导致工作线程等待,而另外一个则不会
//Control的Invoke和BeginInvoke的参数为delegate,
//delegate中的方法是在Control的线程中执行的,即UI线程中執行
#region form界面还是会有卡顿
for (int i = 0; i < 1200; i++)
{
//Console.WriteLine($"计数={i}");//控制台这样是异步,界面无卡顿'
//BeginInvoke 卡斯界面,报错
// BeginInvoke 表示异步,它是相对于调用线程的异步,而不是相对于UI线程的异步。
//this.label1.BeginInvoke(new Action(() =>
//{
// this.label1.Text = i.ToString();
// Application.DoEvents();
// Thread.Sleep(100);
//}));
//Invoke正常
//this.label1.Invoke(new Action(() =>
//{
// this.label1.Text = i.ToString();
// Application.DoEvents(); //加了后可以实时刷新label,不加则是同步
// Thread.Sleep(100);
//}));
//Invoke跟新界面是同步 如果改用BeginInvoke会卡斯界面,报错
this.Invoke(new Action(() =>
{
this.textBox1.AppendText(content + "\r\n");
this.label1.Text = i.ToString();
Application.DoEvents(); //加了后可以实时刷新label,不加则是同步
Thread.Sleep(100);
})
);
}
#endregion
//控制台这样是异步,界面无卡顿
//Console.WriteLine(" Invoke thread id is:" + Thread.CurrentThread.ManagedThreadId.ToString());
}
private void DoSomeThingToXunhuan(string content)
{
Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ***Console执行 {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
//this.textBox1.AppendText不加到invoke中是不会及时更新界面的,因为这里是多线程了
//this.textBox1.AppendText(
// $@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ,{Thread.CurrentThread.ManagedThreadId.ToString("00")}" + "\r\n");
//Invoke跟新界面是同步就是回到主线程执行 如果嵌套在循环中,改用BeginInvoke会卡斯界面,报错
this.Invoke((MethodInvoker)delegate
{
Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ***执行 {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
this.textBox1.AppendText(
$@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ,{ content}" + "\r\n");
this.label1.Text = $@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ,{ content}";
//如果没有加上 DoEvents的话,由于循环时间会比较久就会出现假死的状态
//,而且程序不能处理其他的事件。而如果加上DoEvents的话就会对文本框的值实时响应
//,给用户带来较好的用户体验,可是DoEvents也带来了效率上的问题,
// 处理同样的一个事件调用了DoEvents后效率降低了好几倍,这也是为什么要慎用的原因,能不用尽量不用
Application.DoEvents(); //循环中加了后可以实时刷新label,不加则是同步
Thread.Sleep(1000);
});
}
private void DoSomeThingYIBU02(string content)
{
//Invoke跟新界面是同步 如果史嵌套在循环中,改用BeginInvoke会卡斯界面,报错
this.Invoke((MethodInvoker)delegate
{
Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ******开始执行");
this.textBox1.AppendText(
$@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ,{ content}" + "\r\n");
this.label1.Text = $@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ,{ content}";
//如果没有加上 DoEvents的话,由于循环时间会比较久就会出现假死的状态
//,而且程序不能处理其他的事件。而如果加上DoEvents的话就会对文本框的值实时响应
//,给用户带来较好的用户体验,可是DoEvents也带来了效率上的问题,
// 处理同样的一个事件调用了DoEvents后效率降低了好几倍,这也是为什么要慎用的原因,能不用尽量不用
//Application.DoEvents(); //循环中加了后可以实时刷新label,不加则是同步
Thread.Sleep(1000);
});
//this.textBox1.BeginInvoke((MethodInvoker)delegate
//{
// this.textBox1.AppendText(content + "\r\n");
//});
}
/// <summary>
/// 委托异步测试1
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnYibu_Click(object sender, EventArgs e)
{
this.AddContentTongbu($"btnYibu_Click 开始 {Thread.CurrentThread.ManagedThreadId}");
Action<string> action = this.DoSomeThingLongYIBU;
//在委托中循环
//action.BeginInvoke=异步多线程
action.BeginInvoke($"btnYibu_Click,ManagedThreadId= {Thread.CurrentThread.ManagedThreadId}",null,null);
this.label1.Text = Thread.CurrentThread.ManagedThreadId.ToString();
this.AddContentTongbu($"btnYibu_Click 结束 {Thread.CurrentThread.ManagedThreadId}");
}
/// <summary>
/// 委托异步测试2
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnYibu2_Click(object sender, EventArgs e)
{
this.AddContentTongbu($"btnYibu_Click2 开始 {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
Action<string> action = this.DoSomeThingToXunhuan;
for (int i = 0; i < 1000; i++)
{
//DoSomeThingYIBU02 Thread.Sleep(100);执行一次
//action.BeginInvoke=异步多线程
action.BeginInvoke($"Yibu_{i},ManagedThreadId= {Thread.CurrentThread.ManagedThreadId.ToString("00")}", null, null);
// Thread.Sleep(50);//加上就卡死
// 这里一运行就会显示 01=999 ,因为上面是异步
this.label2.Text = $"{Thread.CurrentThread.ManagedThreadId.ToString("00")}={i}";
}
this.AddContentTongbu($"btnYibu_Click 结束 {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
}
/// <summary>
/// 线程更新
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnThread_Click(object sender, EventArgs e)
{
this.label1.Text = "";
this.AddContentTongbu($"btnThread_Click 开始 {Thread.CurrentThread.ManagedThreadId}");
//启动一个线程
Thread thread = new Thread(new ThreadStart(ThDoWork));
thread.IsBackground = true;
thread.Start();
//从ThreadPool池中取线程,ThreadPool会根据限制的数量去申请与释放
//ThreadPool.SetMaxThreads(100);
//ThreadPool.SetMaxThreads(10);
WaitCallback waitCallback = o =>
{
for (int i = 0; i < 1000; i++)
{
Console.WriteLine("waitCallback thread id is:" + Thread.CurrentThread.ManagedThreadId.ToString());
//Invoke跟新界面是同步 如果实在循环内,改用BeginInvoke会卡斯界面
this.Invoke(new Action(() =>
{
//Invoke thread id is:1 ,,,回到UI主線程
Console.WriteLine("waitCallback Invoke thread id is:" + Thread.CurrentThread.ManagedThreadId.ToString("00"));
this.label1.Text = "计数"+ i.ToString();
this.textBox1.AppendText($"计数{i},线程ID={Thread.CurrentThread.ManagedThreadId.ToString("00")}" + "\r\n");
Console.WriteLine("waitCallback Invoke thread id is!" + Thread.CurrentThread.ManagedThreadId.ToString("00"));
}));
Console.WriteLine("waitCallback Done!");
};
//取参数值
object[] obj = o as object[];
Console.WriteLine(obj[0].ToString()+" " + obj[1].ToString());
};
ThreadPool.QueueUserWorkItem(waitCallback,new object[]{"haha","ohye" });
#region Parallel并发
//Parallel可以启动多线程,但主线程也参与计算,节约一个线程
//ParallelMergeOptions可以轻松最大并发多大并发数
//Parallel.Invoke(
// () => { },
// () => { },
// () => { }
// );
#endregion
}
public void DoWork()
{
for (int i = 0; i < 1000; i++)
{
Console.WriteLine("thread id is:" + Thread.CurrentThread.ManagedThreadId.ToString()); //Task thread id is: 4 ,,,新的線程
//Invoke跟新界面是同步 如果实在循环内,改用BeginInvoke会卡斯界面
this.Invoke(new Action(() =>
{
Console.WriteLine(" Invoke thread id is:" + Thread.CurrentThread.ManagedThreadId.ToString()); //Invoke thread id is:1 ,,,回到UI主線程
this.label1.Text = i.ToString();
this.textBox1.AppendText($"计数{i},线程ID={Thread.CurrentThread.ManagedThreadId}" + "\r\n");
Console.WriteLine("Invoke1!");
}));
Console.WriteLine("Done!"); // 上面語句如果是BeginInvoke,則這個及后面語句不會被阻塞,它的輸出在“Invoke1”,“Invoke2”之前。反之,用Invoke則會,它的輸出在之後。
}
}
/// <summary>
/// 如果是thread調用,注意参数是object。task 調用的話就無謂。
/// </summary>
public void ThDoWork()
{
Console.WriteLine("开始 thread id is:" + Thread.CurrentThread.ManagedThreadId.ToString("00")); //Task thread id is: 4 ,,,新的線程
//如果是执行一次可以用 BeginInvoke //Invoke跟新界面是同步 如果改用BeginInvoke会卡斯界面
this.BeginInvoke(new Action(() =>
{
Console.WriteLine(" begin thread id is:" + Thread.CurrentThread.ManagedThreadId.ToString()); //Invoke thread id is:1 ,,,回到UI主線程
this.textBox1.AppendText($"begin 线程ID={Thread.CurrentThread.ManagedThreadId.ToString("00")}" + "\r\n");
// Thread.Sleep(2000);加了延迟主线程会卡住2秒
this.textBox1.AppendText($"end 线程ID={Thread.CurrentThread.ManagedThreadId.ToString("00")}" + "\r\n");
Console.WriteLine(" end thread id is:" + Thread.CurrentThread.ManagedThreadId.ToString()); //Invoke thread id is:1 ,,,回到UI主線程
}));
Thread.Sleep(2000);
Console.WriteLine("结束 thread id is:" + Thread.CurrentThread.ManagedThreadId.ToString("00")); // 上面語句如果是BeginInvoke,則這個及后面語句不會被阻塞,它的輸出在“Invoke1”,“Invoke2”之前。反之,用Invoke則會,它的輸出在之後。
}
/// <summary>
/// 控制委托执行顺序
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnCtrlOrder_Click(object sender, EventArgs e)
{
this.AddContentTongbu($@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 控制委托执行顺序开始 {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
Action<string> action = this.DoSomeThingYIBU02;
//委托执行完成后的回调函数 异步回调
AsyncCallback asyncCallback = ar =>
{
Console.WriteLine($"委托执行完成后的回调函数 异步回调 {Thread.CurrentThread.ManagedThreadId.ToString("00")},参数={ar.AsyncState}");// "sunday"
Console.WriteLine($"委托执行完成后的回调函数 异步回调 {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
//放到主线程钟更新textBox1
this.Invoke(new Action(() =>
{
this.textBox1.AppendText(
$@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 委托执行完成后的回调函数 异步回调 {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
}));
};
//action.BeginInvoke=异步线程
action.BeginInvoke($@" 控制委托执行顺序 BeginInvoke", asyncCallback, "sunday");
this.AddContentTongbu($@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}控制委托执行顺序 结束 {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
}
private void btnUpLoadProcess_Click(object sender, EventArgs e)
{
this.AddContentTongbu($@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 文件上传开始 {Thread.CurrentThread.ManagedThreadId}");
Action<string> action = this.YiBuShangChuanWenian;
//action.BeginInvoke=异步线程
IAsyncResult asyncResult= action.BeginInvoke($@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} YiBuShangChuanWenian IAsyncResult", null, null);
int i = 0;
//这种方法是柱塞界面的
while (!asyncResult.IsCompleted)//开始是false 委托执行完成后会改为true
{
if (i < 9)
{
ShowlblProccessText( $@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {++i*10}%");
}
else
{
ShowlblProccessText($@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 99%");
}
Thread.Sleep(200);
//无什么没有立即跟新界面,因为主线程在忙,忙完了才更新label,需要让主线程闲下来,其他操作由子线程去完成
}
//这里会有200毫秒的误差延迟
//asyncResult 执行完了以后,才会执行下面这一句
this.AddContentTongbu($@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 完成文件上传 {Thread.CurrentThread.ManagedThreadId}");
}
private void ShowlblProccessText(string msg)
{
Console.WriteLine($"{msg},{Thread.CurrentThread.ManagedThreadId.ToString("00")} ");
this.Invoke(new Action(() =>
{
lblProccess.Text = msg;
// Application.DoEvents();好性能,能不用尽量不用
}));
}
private void YiBuShangChuanWenian(string content)
{
for (int i = 0; i < 20; i++)
{
//Invoke跟新界面是同步 如果史嵌套在循环中,改用BeginInvoke会卡斯界面,报错
this.Invoke((MethodInvoker)delegate
{
Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ******开始执行--{i}");
this.textBox2.AppendText(
$@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}----{i} ,{ content}" + "\r\n");
this.label2.Text = content;
Application.DoEvents(); //加了后可以实时刷新label,不加则是同步
Thread.Sleep(500);
});
}
}
private void btnXinHaoLiang_Click(object sender, EventArgs e)
{
this.AddContentTongbu($@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 文件上传开始 {Thread.CurrentThread.ManagedThreadId}");
Action<string> action = this.YiBuShangChuanWenian2;
//action.BeginInvoke=异步线程
var asyncResult = action.BeginInvoke("调用接口", null, null);
// 并发执行下面的Console.WriteLine
Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
//阻塞当前线程,直到收到asyncResult发出的信号量,无延迟
asyncResult.AsyncWaitHandle.WaitOne();
// asyncResult.AsyncWaitHandle.WaitOne(5000); //等待且做多等待5秒,超过就过,就会执行下一行代码,作用相当与超时设置,当要
//执行5个任务时,有一个任务很慢,会影响整个流程,可以做超时控制,超时就换下一个任务。
ShowlblProccessText($@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 99%");
//asyncResult 执行完了以后,才会执行下面这一句
this.AddContentTongbu($@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 完成文件上传 {Thread.CurrentThread.ManagedThreadId}");
}
private void YiBuShangChuanWenian2(string content)
{
//这里不能使用 this.Invoke 和 BeginInvoke 来跟新 ,
// 因为 asyncResult.AsyncWaitHandle.WaitOne(); 柱塞了当前线程
//this.BeginInvoke(new Action(() =>
//{
Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {content}**执行1");
this.textBox2.AppendText(
$@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}---{content}" + "\r\n");
this.label2.Text = content;
// Application.DoEvents(); //加了后可以实时刷新label
Thread.Sleep(1000);
Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {content} **执行2");
//}));
}
/// <summary>
/// 异步委托调用的返回值
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnAsyncResult_Click(object sender, EventArgs e)
{
Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 开始");
Func<int> fc = this.RemoteService;
IAsyncResult asyncResult = fc.BeginInvoke(null, null);//asyncResult是描述异步调用状态
int result = fc.EndInvoke(asyncResult);//获取委托异步返回值,只能用EndInvoke
#region 也可以在回调里面拿到结果
//简写
Func<string, string> func2 = s => $"1+{s}";
string result2 = func2.EndInvoke(func2.BeginInvoke("func2的字符串参数", null, null));
Console.WriteLine($"简写 = {result2}");
//回调
IAsyncResult asyRes = fc.BeginInvoke(ar =>
{
int Result4 = fc.EndInvoke(ar);// 拿到RemoteService 的计算结果 只能在回调里面拿到结果
Console.WriteLine($"回调 = {Result4}");
}, null);
//int Result5 = fc.EndInvoke(asyRes);//每个异步操作,只能使用一次EndInvoke
#endregion
Console.WriteLine($"result = {result}");
Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 结束");
}
private int RemoteService()
{
long result=0;
for (int i = 0; i < 100000000; i++)
{
//Console.WriteLine($"{i}");
result += i;
}
return DateTime.Now.Day;
}
#region task是多线程的最佳实践
//task全部是线程池的线程
private void btnTask_Click(object sender, EventArgs e)
{
// Action action = () =>
//{
// Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} **执行1");
// this.textBox2.AppendText(
// $@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}" + "\r\n");
// // Application.DoEvents(); //加了后可以实时刷新label
// Thread.Sleep(1000);
// Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} **执行2");
//};
// Task task = new Task(action);
// task.Start();
//1,柱塞界面
//List<Task> taskList = new List<Task>();
//taskList.Add(Task.Run(()=> { this.Coding("AAA"); }));
//taskList.Add(Task.Run(()=> { this.Coding("BBB"); }));
//taskList.Add(Task.Run(()=> { this.Coding("CCC"); }));
//taskList.Add(Task.Run(() => { this.Coding("DDD"); }));
任一任务执行完成就执行后面的代码
//Task.WaitAny(taskList.ToArray());
//Console.WriteLine("有一个任务完成");
等待所有的任务完成,柱塞界面
//Task.WaitAll(taskList.ToArray());
//2,这样新启动一个线程就不卡界面了,尽量不要线程套线程,会出bug
//Task.Run(() =>
//{
// List<Task> taskList = new List<Task>();
// taskList.Add(Task.Run(() => { this.Coding("AAA"); }));
// taskList.Add(Task.Run(() => { this.Coding("BBB"); }));
// taskList.Add(Task.Run(() => { this.Coding("CCC"); }));
// taskList.Add(Task.Run(() => { this.Coding("DDD"); }));
// //任一任务执行完成就执行后面的代码
// Task.WaitAny(taskList.ToArray());
// Console.WriteLine("有一个任务完成");
// //等待所有的任务完成,柱塞界面
// Task.WaitAll(taskList.ToArray());
//});
//3,不柱塞界面
List<Task> taskList = new List<Task>();
taskList.Add(Task.Run(() => { this.Coding("AAA"); }));
taskList.Add(Task.Run(() => { this.Coding("BBB"); }));
taskList.Add(Task.Run(() => { this.Coding("CCC"); }));
taskList.Add(Task.Run(() => { this.Coding("DDD"); }));
TaskFactory taskFactory = new TaskFactory();
//等待任一任务完成,启动新的task来完成后续的动作
//taskFactory.ContinueWhenAny(taskList.ToArray(), t =>
//{
// Console.WriteLine($"有一个任务完成,{Thread.CurrentThread.ManagedThreadId.ToString("00")}");
//});
//等待所有任务完成,启动新的task来完成后续的动作
//taskFactory.ContinueWhenAll(taskList.ToArray(), tArray =>
//{
// Console.WriteLine($"任务全部完成,{Thread.CurrentThread.ManagedThreadId.ToString("00")}");
//});
//Continue 后面的task有可能是新线程,有可能是taskList的线程,不可能是主线程
//线程顺序是不可预测的
//如果想最后执行 收取费用需要这样使用taskList.Add,taskFactory.ContinueWhenAll启动新的task来完成后续的动作
taskList.Add(taskFactory.ContinueWhenAll(taskList.ToArray(), tArray =>
{
Console.WriteLine($"任务全部完成," +
$"{Thread.CurrentThread.ManagedThreadId.ToString("00")}");
})
);
Task.WaitAll(taskList.ToArray());
Console.WriteLine($"任务全部完成,收取费用");
}
private void Coding(string str)
{
long result = 0;
for (int i = 0; i < 200000000; i++)
{
result += i;
}
string content = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {str}*线程ID={Thread.CurrentThread.ManagedThreadId.ToString("00")}*执行";
//this.textBox2.AppendText(content);
Console.WriteLine(content);
}
#endregion
/// <summary>
/// 线程异常处理
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnTaskMore_Click(object sender, EventArgs e)
{
//发生异常后,对其他线程无影响
for (int i = 0; i < 100; i++)
{
string name = $"btnTaskMore_Click{i}";
Task.Run(() =>
{
try
{
if (name.Equals("btnTaskMore_Click10"))
{
throw new Exception("btnTaskMore_Click10 异常");
}
else if (name.Equals("btnTaskMore_Click15"))
{
throw new Exception("btnTaskMore_Click15 异常");
}
else if (name.Equals("btnTaskMore_Click20"))
{
throw new Exception("btnTaskMore_Click20 异常");
}
Console.WriteLine($"{name}");
}
catch (Exception ex )
{
Console.WriteLine("记录异常日志 "+ ex.Message);
}
});
}
}
private static readonly object LOCK = new object();
private void btnTaskSafe_Click(object sender, EventArgs e)
{
// i循环会输出5 变量临时冲突 ,k输出0,1,2,3,4
for (int i = 0; i < 5; i++)
{
int k = i;//这里5次循环创建5个I变量,闭包作用
Task.Run(() =>
{
//每次循环i都是5 而k=0,1,2,3,4
Console.WriteLine($" this is i={i},k={k}");
});
}
//多线程访问集合一般不会有安全问题,线程安全都是出在修改问题的时候。
#region 解决线程安全问题
List<int> intlist = new List<int>();
for (int i = 0; i < 10000; i++)
{
Task.Run(() =>
{
lock (LOCK)
{
intlist.Add(i);
}
});
}
// 加lock解决线程安全问题,保证lock方法块内,任一时刻只有一个线程能进去,其他线程排队。
Thread.Sleep(3000);
Console.WriteLine(intlist.Count) ;
#endregion
}
/// <summary>
/// 线程异常处理2
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnTaskEx2_Click(object sender, EventArgs e)
{
CancellationTokenSource cts = new CancellationTokenSource();//cts.IsCancellationRequested默认false;
List<Task> tasklist = new List<Task>();
for (int i = 0; i < 100; i++)
{
string name = $"btnTaskMore_Click{i}";
tasklist.Add(Task.Run(() =>
{
try
{
Thread.Sleep(new Random().Next(50, 100) * 10);
if (!cts.IsCancellationRequested)
{
Console.WriteLine($" {name} 开始执行");
if (name.Equals("btnTaskMore_Click10"))
{
throw new Exception("btnTaskMore_Click10 异常");
}
else if (name.Equals("btnTaskMore_Click15"))
{
throw new Exception("btnTaskMore_Click15 异常");
}
else if (name.Equals("btnTaskMore_Click20"))
{
throw new Exception("btnTaskMore_Click20 异常");
}
}
if (!cts.IsCancellationRequested)
{
Console.WriteLine($" {name} 结束执行");
}
else
{
Console.WriteLine($"{name} 结束执行");
}
}catch(Exception ex)
{
Console.WriteLine(ex.Message);
cts.Cancel();
}
} )
);
}
Task.WaitAll(tasklist.ToArray());
}
/// <summary>
/// 线程取消
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnThreadCancel_Click(object sender, EventArgs e)
{
try
{
CancellationTokenSource cts = new CancellationTokenSource();//cts.IsCancellationRequested默认false;
List<Task> tasklist = new List<Task>();
for (int i = 0; i < 100; i++)
{
string name = $"btnTaskMore_Click{i}";
tasklist.Add(Task.Run(() =>
{
try
{
Thread.Sleep(new Random().Next(50, 100) * 10);
if (!cts.IsCancellationRequested)
{
Console.WriteLine($" {name} 开始执行");
if (name.Equals("btnTaskMore_Click10"))
{
throw new Exception("btnTaskMore_Click10 异常");
}
else if (name.Equals("btnTaskMore_Click15"))
{
throw new Exception("btnTaskMore_Click15 异常");
}
else if (name.Equals("btnTaskMore_Click20"))
{
throw new Exception("btnTaskMore_Click20 异常");
}
}
if (!cts.IsCancellationRequested)
{
Console.WriteLine($" {name} 结束执行");
}
else
{
Console.WriteLine($"{name} 结束执行");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
cts.Cancel();
}
}, cts.Token)
);
}
Task.WaitAll(tasklist.ToArray());
}
catch (AggregateException aex)
{
// CancellationTokenSource 终止线程,可以让没有启动的线程取消
foreach (var item in aex.InnerExceptions)
{
Console.WriteLine(item.Message);
}
}
catch (Exception ext)
{
Console.WriteLine(ext.Message);
}
}
}
}
c# 异步委托和task多线程实战
最新推荐文章于 2024-02-27 13:57:05 发布