C# 将循环转换为任务

C# 将循环转换为任务,c#,asynchronous,async-await,task-parallel-library,c#-5.0,C#,Asynchronous,Async Await,Task Parallel Library,C# 5.0,我有以下同步代码: foreach ( var step in result ) { step.Run(); } 我试图将其转换为任务,但失败了。我尝试使用Task转换它。当所有的都是这样时(我确实在方法签名中附加了async): 这会永远阻塞。但是,当我在循环中创建一个 foreach ( var step in result ) { var t = Task.Run( () => step.Run() ); t.Wait(); } 如果改用wait Task.R

我有以下同步代码:

foreach ( var step in result ) {
  step.Run();
}
我试图将其转换为任务,但失败了。我尝试使用
Task转换它。当所有的
都是这样时(我确实在方法签名中附加了async):

这会永远阻塞。但是,当我在循环中创建一个

foreach ( var step in result ) {
    var t = Task.Run( () => step.Run() );
    t.Wait();
}
如果改用
wait Task.Run(()=>step.Run())它只等待第一个线程并恢复主线程

run方法如下所示:

public async void Run() {
    var result = Work();
    if ( null != result && result.Count > 0 ) {
        var tasks = new List<Task>();
        foreach ( var step in result ) {
            await Task.Run( () => step.Run() );
        }
    }
}
class NoWorkStep : WorkerStep {
    protected override IList<WorkerStep> Work() {
        Console.WriteLine( "HERE" );
        List<WorkerStep> newList = new List<WorkerStep>();
        for ( int i = 0; i < 10; i++ ) {
            newList.Add( new NoWorkStep2() );
        }
        return newList;
    }
}
class NoWorkStep2 : WorkerStep {
    protected override IList<WorkerStep> Work() {
        Console.WriteLine( "HERE-2" );
        return new List<WorkerStep>();
    }
}

让我们找出代码中的问题:

new Task(() => step.Run())
这将返回一个冷的
任务
,意味着
任务
实际上没有启动。为了启动,您需要呼叫:

new Task(() => step.Run()).Start)
但是,您不应该使用
newtask
,无论如何,您应该使用
Task.Run

如果改用wait Task.Run(()=>step.Run());它只在等待 第一个,然后继续主线程

这是因为
Run
async void
,不能等待
async void
仅在顶级事件处理程序中使用,这里显然不是这样

如果要等待所有任务完成,可以执行以下操作:

public async Task RunAsync() 
{
    var result = Work();
    var stepTasks = result.Select(step => Task.Run(() => step.Run()));
    await Task.WhenAll(steps);
}

这将保证所有任务在
RunAsync
完成后都已完成执行。

您似乎没有启动任务

尝试:

var tasks=newlist();
foreach(var步进结果)
{
var t=新任务(()=>step.Run());
t、 Start();
任务。添加(t);
}
任务。WhenAll(任务);

您可以使用
Parallel.ForEach

Parallel.ForEach(result, step => step.Run());

通过这种方式,您甚至不需要处理并行框架的较低级别部分

只要调用Task.Run,就不需要创建冷任务,只需在下一行中启动它。我只是想说明问题中的代码不起作用的原因。冷任务是原始代码的真正问题。很容易忘记启动调用,它们在“Task.Run”或“Task.Factory.StartNew”上不提供任何内容。我将
async void Run
更改为
async Task RunAsync()
,并使用了您的代码片段。我使用
wait step.RunAsync()调用RunAsyncdoIt
?怎么做?我从
static void Main
调用
doIt
,那么,
Main
将在
doIt
有机会完成之前终止。如果在控制台应用程序中运行,则还需要使
doIt
返回
任务
,并使用
doIt().Wait()
。谢谢。现在工作。你是对的,我一看到你的评论就明白了。谢谢你的帮助,这会起作用,也许我会以此结束,但是aync等待模式也应该起作用,我想知道我的错误在哪里。或者确实是
result.AsParallel().ForAll(step=>step.Run()),但除非您有实际的查询,否则您不会真正使用PLINQ。@NathanCooper您是对的,这取决于
结果的类型是否合理。两者都可以,但我喜欢这里的
ForEach()
,因为它保留了初始代码片段的意图。不要使用
newtask
。第四个示例之所以有效,并不是因为
等待
在周期内,而是因为您正在使用
任务。运行
而不是
新任务
new Task(() => step.Run()).Start)
public async Task RunAsync() 
{
    var result = Work();
    var stepTasks = result.Select(step => Task.Run(() => step.Run()));
    await Task.WhenAll(steps);
}
var tasks = new List<Task>();

foreach (var step in result) 
{
    var t = new Task(() => step.Run());
    t.Start();
    tasks.Add(t);
}

Task.WhenAll(tasks);
Parallel.ForEach(result, step => step.Run());