C# &引用;“新线程”;和异步
我使用下面的代码做了一个模拟并发的示例:C# &引用;“新线程”;和异步,c#,multithreading,asynchronous,async-await,C#,Multithreading,Asynchronous,Async Await,我使用下面的代码做了一个模拟并发的示例: var threads = new Thread[200]; //starting threads logic for (int i = 0; i < 200; i++) { threads[i].Start(); } for (int i = 0; i < 200; i++) { threads[i].Join();
var threads = new Thread[200];
//starting threads logic
for (int i = 0; i < 200; i++)
{
threads[i].Start();
}
for (int i = 0; i < 200; i++)
{
threads[i].Join();
}
var threads=新线程[200];
//启动线程逻辑
对于(int i=0;i<200;i++)
{
线程[i].Start();
}
对于(int i=0;i<200;i++)
{
线程[i].Join();
}
该代码应该向数据库中插入数千条记录,而且似乎工作得很好,因为线程几乎同时完成
但是,当我使用:
var tasks = new List<Task<int>>();
for (int i = 0; i < 200; i++)
{
tasks.Add(insert(i));
// await insert(i);
}
int[] result = await Task.WhenAll(tasks);
var tasks=newlist();
对于(int i=0;i<200;i++)
{
增加(插入(i));
//等待插入(i);
}
int[]结果=等待任务.WhenAll(任务);
完成同样的逻辑需要很多时间
有人能给我解释一下有什么区别吗?我认为
Await
应该创建线程。在第一个示例中,您手动创建了线程。在第二秒内,您创建了任务。任务-可能-正在使用线程池,其中线程数量有限。因此,大多数任务都在队列中等待,而很少有任务在可用线程上并行执行。如果需要复制原始的基于线程的行为,可以使用task.Factory.StartNew(…,TaskCreationOptions.longlunning)
来安排工作,然后通过Task.WaitAll
阻塞,直到辅助任务完成。我不推荐这种方法,但就行为而言,这将非常接近您的代码以前的工作方式
下面将对may在您的场景中无法获得预期性能的原因进行更深入的分析:
解释,第1部分(async
并不表示“在不同的线程上”)
用async
关键字标记的方法不会神奇地异步运行。它们仅仅能够将等待的操作(它们本身可能异步运行,也可能不异步运行)组合成一个更大的单元(通常是Task
或Task
)
如果您的insert
方法是async
,则它仍然可能至少同步执行部分工作。第一条wait
语句前面的所有代码都肯定是这种情况。这项工作将在“主”线程(调用insert
的线程)上执行,这将是您的瓶颈或至少是其中的一部分,因为当您在紧循环中调用insert
时,该部分代码的并行度将为1,无论您是否等待生成的任务
为了说明上述观点,请考虑下面的例子:
void Test()
{
Debug.Print($"Kicking off async chain (thread {Thread.CurrentThread.ManagedThreadId}) - this is the main thread");
OuterTask().Wait(); // Do not block on Tasks - educational purposes only.
}
async Task OuterTask()
{
Debug.Print($"OuterTask before await (thread {Thread.CurrentThread.ManagedThreadId})");
await InnerTask().ConfigureAwait(false);
Debug.Print($"OuterTask after await (thread {Thread.CurrentThread.ManagedThreadId})");
}
async Task InnerTask()
{
Debug.Print($"InnerTask before await (thread {Thread.CurrentThread.ManagedThreadId})");
await Task.Delay(10).ConfigureAwait(false);
Debug.Print($"InnerTask after await (thread {Thread.CurrentThread.ManagedThreadId}) - we are now on the thread pool");
}
这将产生以下输出:
Kicking off async chain (thread 6) - this is the main thread
OuterTask before await (thread 6)
InnerTask before await (thread 6)
InnerTask after await (thread 8) - we are now on the thread pool
OuterTask after await (thread 8)
现在使用TaskCreationOptions.LongRunning
:
IEnumerable<Task> tasks = Enumerable
.Range(0, 200)
.Select(_ => Task.Factory.StartNew(
() => Thread.Sleep(100), TaskCreationOptions.LongRunning
));
await Task.WhenAll(tasks); // Completes in under 130 milliseconds.
IEnumerable任务=可枚举
.范围(0,200)
.Select(=>Task.Factory.StartNew(
()=>Thread.Sleep(100),TaskCreationOptions.LongRunning
));
等待任务。当所有(任务);//在130毫秒内完成。
生成200个线程通常不是一个好主意(这将不能很好地扩展),但是如果阻塞调用的大规模并行化是一个绝对要求,那么上面的代码片段向您展示了一种使用TPL的方法。No.await不会创建线程。在此上下文中,“异步”一词与多线程无关。在代码中,您从何处启动任务?您是否知道启动线程会消耗超过1MB的内存?因此,200远远超过200MB。这是不做任何处理的。而且启动线程的速度很慢。从线程池中使用它们要好得多。谢谢大家,您的回复确实是有用的任务(wait/Async),它只是一个非常复杂的状态机。@Erik当然,但是任务是使用线程执行的。TaskSheduker管理任务,并使用线程池中的线程执行任务。谢谢各位,你们的回复真的很有帮助谢谢,你们的回复真的很有帮助
IEnumerable<Task> tasks = Enumerable
.Range(0, 200)
.Select(_ => Task.Factory.StartNew(
() => Thread.Sleep(100), TaskCreationOptions.LongRunning
));
await Task.WhenAll(tasks); // Completes in under 130 milliseconds.