C# 多线程
C# 多线程理解
Task中await和.Result和GetAwaiter().GetResult()方法的区别
await不会阻塞调用它的线程,而GetAwaiter().GetResult()和.Result则会阻塞调用它的线程,但都会阻塞当前线程
Task.Run 和 Task.Factory.StartNew 区别
Task.Run 是在 dotnet framework 4.5 之后才可以使用,但是 Task.Factory.StartNew 可以使用比 Task.Run 更多的参数,可以做到更多的定制。
可以认为 Task.Run 是简化的 Task.Factory.StartNew 的使用,除了需要指定一个线程是长时间占用的,否则就使用 Task.Run
Task.Run 用的是线程池,也就是不是调用这个函数就会一定创建一个新的线程,但是会在另一个线程运行。
Task.Run和Task.Factory.StartNew都会创建一个新的线程
两个函数最大的不同在于 Task.Factory.StartNew 可以设置线程是长时间运行,这时线程池就不会等待这个线程回收
Task.Factory.StartNew(() =>
{
for (int i = 0; i < 100; i++)
{
var foo = 2;
}
Console.WriteLine("进行 线程" + Thread.CurrentThread.ManagedThreadId);
}, TaskCreationOptions.LongRunning);
所以在需要设置线程是长时间运行的才需要使用 Task.Factory.StartNew 不然就使用 Task.Run
调用 Task.Run(foo) 就和使用下面代码一样
Task.Factory.StartNew(foo,
CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
await是阻塞当前方法继续调用,但不阻塞当前线程(返回Task/Task)
使用 .Result 或者 .Wait() ,这样会在阻塞当前线程同时仍然会启动另外一个后台线程去执行这个函数,得不偿失,大部分情况下我们只需要直接使用 await 关键字即可。
在非常特殊的情况下,如果非得阻塞当前线程去执行这个 Task,可以考虑使用 .GetAwaiter().GetResult()。两者效果基本一样,阻塞当前线程同时会启动另外一个后台线程。但是如果使用 .Result 或 .Wait() 发生错误时会抛出 AggregateExecption ,而使用 .GetAwaiter().GetResult(),则是会抛出实际的 Excepiton ,Excepiton 中的堆栈也符合我们所期望的。
主要是区分两个概念:
1.阻塞当前方法调用;
2.阻塞当前线程;
Demo源码
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
Console.WriteLine("我是主线程,线程ID:" + Thread.CurrentThread.ManagedThreadId);
//task用法一
Task task1 = new Task(() => MyAction());
task1.Start();
//task用法二
var strRes = Task.Run<string>(() => { return GetReturnStr(); });
Console.WriteLine(strRes.Result);
Console.WriteLine("---------------------------------");
Console.WriteLine("①我是主线程,线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
var testResult = TestAsync();
testResult.GetAwaiter().OnCompleted(() => {
var id = testResult.Id;
Console.WriteLine("My name is: " + id);
});
//var te = testResult.Result;
Test();
Console.WriteLine("主线程执行完毕");
Console.ReadKey();
}
static async Task TestAsync()
{
Console.WriteLine("②调用GetReturnResult()之前,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId,
DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss.fff"));
var name = GetReturnResult();
Console.WriteLine("④调用GetReturnResult()之后,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId,
DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss.fff"));
Console.WriteLine("⑥得到GetReturnResult()方法的结果一:{0}。当前时间:{1},线程ID:{2}", await name,
DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss.fff"), Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("⑥得到GetReturnResult()方法的结果二:{0}。当前时间:{1},线程ID:{2}", name.GetAwaiter().GetResult(),
DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss.fff"), Thread.CurrentThread.ManagedThreadId);
}
static async Task<string> GetReturnResult()
{
Console.WriteLine("③执行Task.Run之前, 线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
return await Task.Run(() =>
{
Thread.Sleep(1000);
Console.WriteLine("⑤GetReturnResult()方法里面线程ID: {0}", Thread.CurrentThread.ManagedThreadId);
return "我是返回值";
});
}
static void MyAction()
{
Console.WriteLine("我是新进程,线程ID:() MyAction " + Thread.CurrentThread.ManagedThreadId);
}
static string GetReturnStr()
{
return "我是返回值 ,GetReturnStr 线程ID:" + Thread.CurrentThread.ManagedThreadId;
}
static async void Test()
{
Thread.Sleep(2000);
Console.WriteLine("Test 线程ID:" + Thread.CurrentThread.ManagedThreadId);
await Task.Run(() => {
Thread.Sleep(5000);
Console.WriteLine("Test方法等待5秒开始执行");
});
Console.WriteLine("Test 线程ID:" + Thread.CurrentThread.ManagedThreadId);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南