【发布时间】:2020-10-30 13:48:36
【问题描述】:
示例代码:
class Program
{
static void sleepFunc()
{
int before = Thread.CurrentThread.ManagedThreadId;
Thread.Sleep(5000);
int after = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine($"{before} -> sleep -> {after}");
}
static async void delayFunc()
{
int before = Thread.CurrentThread.ManagedThreadId;
await Task.Delay(5000);
int after = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine($"{before} -> delay -> {after}");
}
static void Main(string[] args)
{
List<Thread> threads = new List<Thread>();
for(int i = 0; i < 10; i++)
{
var thread = new Thread(sleepFunc);
thread.Start();
threads.Add(thread);
}
Thread.Sleep(1000); // just to separate the result sections
for (int i = 0; i < 10; i++)
{
var thread = new Thread(delayFunc);
thread.Start();
threads.Add(thread);
}
Console.ReadLine();
}
}
样本输出:
3 -> sleep -> 3
7 -> sleep -> 7
4 -> sleep -> 4
5 -> sleep -> 5
6 -> sleep -> 6
8 -> sleep -> 8
9 -> sleep -> 9
10 -> sleep -> 10
11 -> sleep -> 11
12 -> sleep -> 12
21 -> delay -> 25
18 -> delay -> 37
15 -> delay -> 36
16 -> delay -> 32
19 -> delay -> 24
20 -> delay -> 27
13 -> delay -> 32
17 -> delay -> 27
22 -> delay -> 25
14 -> delay -> 26
Thread.Sleep() 的延续在同一个显式创建的线程上运行,但 Task.Delay() 的延续在不同的(线程池)线程上运行。
由于延续总是在线程池上运行,如果 func 内的任何地方都有等待,那么使用新的 Thread(func) 是否只是没有意义/浪费/反模式?有没有办法强制任务在原来的非线程池新线程上继续?
我问是因为我读到有人建议在线程中而不是在任务中放置一个长时间运行的循环(例如用于套接字通信),但似乎如果他们调用 ReadAsync 那么它最终会成为一个任务反正线程池,新的线程没意义?
【问题讨论】:
-
你问的是
ConfigureAwait(true)吗?我不确定您所说的“原始新线程”是什么意思-它是原始的还是新的?对我来说,这些有点相反的东西.. -
是的,在所描述的情况下,这是毫无意义的。假设延续将在同一个线程上运行。您的代码等待异步操作完成,然后才能继续执行其他操作。在这种情况下,如果当前线程还想继续运行,那么当异步操作正在进行时,它会做什么?没什么,就是等。所以这和只是做
Thread.Sleep一样。 -
前段时间我有same problem。教父本人斯蒂芬·克利里(Stephen Cleary)向我暗示了Nito.AsyncEx.Context。这个库工作正常——唯一的问题是你应该删除所有
ConfigureAwait-calls,因为这会破坏线程绑定。由于您不知道在被调用的方法中调用了哪些代码,您应该简单地删除所有这些代码。 -
您的示例代码包含一个
async void方法。异步无效is something to avoid.
标签: c# multithreading asynchronous async-await