C# 多线程的几种操作方式(异步委托、Thread、ThreadPool、Task【async/await】)

编程入门 行业动态 更新时间:2024-10-27 18:20:34

C# 多线程的<a href=https://www.elefans.com/category/jswz/34/1769370.html style=几种操作方式(异步委托、Thread、ThreadPool、Task【async/await】)"/>

C# 多线程的几种操作方式(异步委托、Thread、ThreadPool、Task【async/await】)

多线程主要用于多个任务并行执行,可以异步执行任务,提高响应速度,不阻塞当前线程(如C/S窗口)。

使用异步来调用以下代码:

     //用于委托调用private void DoSomething(string name){Console.WriteLine($"DoSomething被调用 {name}");}

一、委托异步调用: core中似乎不支持了

C#使用多线程离不开委托,定义一个委托也是可以直接使用多线程的。

     Action<string> action = this.DoSomething;action.Invoke("张三来了"); //同步调用action("张三来了"); //效果与上面的一样AsyncCallback asyncCallback = result =>{Console.WriteLine($"asyncCallback被执行 {result.AsyncState}");};//参数一:委托方法的入参//参数二:异步执行完之后的回调函数(也是个委托)//参数三:需要传给回调函数的数据(object类型),用result.AsyncState来获取var asyncResult =   action.BeginInvoke("李四来了", asyncCallback,  "小二也来了");//异步调用action.EndInvoke(asyncResult);//阻塞异步,直到异步执行完成。如果是有返回值的异步调用(fun<>)。这里可以接收到。asyncResult.AsyncWaitHandle.WaitOne();//阻塞异步,直到异步执行完成。效果与上面一样,但是没有返回值asyncResult.AsyncWaitHandle.WaitOne(-1);//阻塞异步,直到异步执行完成。效果与上面一样asyncResult.AsyncWaitHandle.WaitOne(1000);//阻塞异步,但是只阻塞1000毫秒,超时就不等了bool isCompleted = asyncResult.IsCompleted;//异步是否执行完成,可以使用while()来循环等待,如下所示:int i = 0;while (!asyncResult.IsCompleted){Thread.Sleep(200);if(i<9)Console.WriteLine($"操作正在进行中。。。已完成{(++i)*10}%");elseConsole.WriteLine($"操作正在进行中。。。已完成99.99%");}Console.WriteLine($"操作已完成");

二、Thread类库

Thread是C#对线程操作封装好的工具类。

int result = 0; //假如 DoSomething 有返回值,用于接收返回值: thread.Join(); 之后使用result。
Thread thread = new Thread(() => {DoSomething("王五来了");result = 5;
});
//不常用方法
//thread.Suspend();//暂停线程,不建议使用
//thread.Resume();//恢复赞停的线程,不建议使用
//thread.Abort(); //销毁线程,主线程让子线程抛出异常的方式来结束线程,可能会有延时,不一定能真的停下来。不建议使用
//Thread.ResetAbort();//恢复已销毁线程,不建议使用//常用方法
while (thread.ThreadState != ThreadState.Stopped)//等待线程执行完成
{Thread.Sleep(200);
}
thread.Join();//运行这句代码的线程,等待thread完成。 可以在主线程等待,也可以在其它子线程等待
thread.Join(1000);//最多只等到1000ms
thread.Priority = ThreadPriority.Highest;//设置线程的执行优先级
//是否后台线程,false:非后台线程(进程关闭,子线程执行完才退出),true:是后台线程(进程关闭,子线程也退出)
thread.IsBackground = false;//模拟异步回调:其实说白了就是异步执行完了再执行另一个函数。
Thread thread1 = new Thread(() => {DoSomething("王五来了");DoSomething("这是回调");
});

三、ThreadPool

线程池(ThreadPool),创建了多个线程对象,需要用的时候直接从线程池中取,避免了对象的创建和销毁等代价(享元设计模式)。节约资源 提升性能,控制线程数量,防止滥用。

ThreadPool.QueueUserWorkItem((obj) => DoSomething("王五来了") );//委托的参数obj就是QueueUserWorkItem的第二个参数。
ThreadPool.QueueUserWorkItem((obj) => DoSomething($"王五来了({obj.ToString()})"),"王五的儿子");//获取线程池中辅助线程的最大数量(workerThreadsMax)和线程池中异步I/O线程的最大数量(completionPortThreadsMax)
//获取线程池中辅助线程的最小数量(workerThreadsMin)和线程池中异步I/O线程的最小数量(completionPortThreadsMin)
ThreadPool.GetMaxThreads(out int workerThreadsMax, out int completionPortThreadsMax);
ThreadPool.GetMinThreads(out int workerThreadsMin, out int completionPortThreadsMin);//设置最大线程数量 和 设置最小线程数量,在进程内是全局的。在一个地方设置了,后面所有的请求中都是这个数量了
//委托异步调用、Task、Parallel、async/await 都使用的是线程池的线程; new Thread()不受限制,但是会占用线程池的数量。
ThreadPool.SetMaxThreads(12, 12);//不能低于当前电脑的线程数;比如四核八线程,就不能低于8,否则无效
ThreadPool.SetMinThreads(1, 1);//线程等待,需要使用ManualResetEvent来完成
ManualResetEvent mre = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem((obj) => {DoSomething("王五来了");mre.Set();
} );
mre.WaitOne();

四、Task

Task是基于任务的异步编程模型。使用的还是线程池里面的线程。

使用Task的时候应该尽量结合async和await关键字来使用。避免使用.Result 和 .Wait()来阻塞等待;

.Result 和 .Wait()会占用线程资源,直到任务完成。

而await的基于异步回调的,不会浪费CPU资源;async和await是语法糖,本质上其实是ContinueWith()。

//*********************************Task使用方式1*******************************************
Task task1 = new Task(() => { DoSomething("张三"); });
task1.Start();//*********************************Task使用方式2*******************************************
var taskFactory = Task.Factory;
Task<int> t1 = taskFactory.StartNew<int>(() => { DoSomething("王五"); return 1; });
Task t2 = taskFactory.StartNew(() => { DoSomething("赵六"); });Task t3 = taskFactory.ContinueWhenAll(new Task[] { t1, t2 }, (t) =>{Console.WriteLine("所有线程都完成了,就会调用这个函数, 不会阻塞主线程");});
Task t4 = taskFactory.ContinueWhenAny(new Task[] { t1, t2 }, (t) =>
{Console.WriteLine("任意一个线程完成了,就会调用这个函数, 不会阻塞主线程");
});//*********************************Task使用方式3*******************************************
Task<int> task = Task.Run<int>(() => { DoSomething("李四"); return 1; }); //使用方式3
int temp = task.Result; //阻塞执行完毕并获取结果,不建议使用这种方式
task.Wait(); //阻塞,直到子线程执行完毕,不建议使用这种方式
await task; //阻塞,直到子线程执行完毕。 但是主线程(调用方)将继续往下执行,await task后面的代码等同于封装在ContinueWith()里面//*********************************Task其它使用方式*******************************************//Task.Delay(2000)不阻塞当前线程,一般配合ContinueWith使用,在ContinueWith里面的子线程将等待2秒之后执行//Thread.Sleep(2000)是阻塞当前线程Task task3 = Task.Delay(2000).ContinueWith((t) =>
{
});//等待所有线程完成
Task.WaitAll(new Task[] { task1, t1 });
//等待任意一个线程完成
Task.WaitAny(new Task[] { task1, t1 });
//线程回调
task1.ContinueWith((o) =>
{Console.WriteLine("线程回调,task1执行完毕之后执行这里。");
});

五、async/await(C/S不建议使用)

async/await是C#保留关键字,需要结合Task使用,async/await是语法糖,本质上其实是ContinueWith()。

使用await Task可以充分的利用有限的CPU资源。在进行IO操作的时候应该尽量使用await Task。

async用于标识异步方法,方法内部有两种情况:

1、没有await:调用方降等待异步方法执行完毕才能接着往下执行,异步方法中的子线程还是并行执行的。

2、包含await:调用方执行异步方法,遇到await后,调用方可以立马往下执行,不需要等待异步方法执行完毕,这个时候“子线程和await task后面的代码”与“调用方”是并行执行的。这里的await指的是在创建子线程(Task.Run)的地方await,且调用方调用异步方法的时候前面没有加await。但是异步函数内部执行到await关键字后会等待子线程执行完毕。

      感觉就像是将await后面的代码封装到ContinueWith()里面执行一样。

使用Task.Run()会创建一个子线程。前面加await会把后面的代码封装成异步回调函数,也是一个新的子线程。使用.Result 或者.Wait()不会像await一样创建子线程。

更多推荐

C# 多线程的几种操作方式(异步委托、Thread、ThreadPool、Task【async/await】)

本文发布于:2024-02-26 20:58:27,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1703882.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:几种   多线程   操作   方式   Thread

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!