C# “返回”;等待任务<;字符串>;。运行(……)”;有时悬挂

C# “返回”;等待任务<;字符串>;。运行(……)”;有时悬挂,c#,async-await,parallel.foreach,C#,Async Await,Parallel.foreach,这是我的代码的复制,其中等待Task.Run(..)中的返回有时会挂起。如果失败,主要是第一次呼叫 我怎样才能改进它 using System.Threading.Tasks; using System.Diagnostics; private void Button_Click(object sender, RoutedEventArgs e) { // This can be a very huge list string[] servers = new string[]

这是我的代码的复制,其中
等待Task.Run(..)
中的返回有时会挂起。如果失败,主要是第一次呼叫

我怎样才能改进它

using System.Threading.Tasks;
using System.Diagnostics;

private void Button_Click(object sender, RoutedEventArgs e)
{
    // This can be a very huge list
    string[] servers = new string[] { "10.17.100.1", "10.17.100.10", "10.17.100.20" };

    // the max parallel tasks must be limited
    Parallel.ForEach(servers,
        new ParallelOptions { MaxDegreeOfParallelism = 10 },  
        (forServer) =>
    {
        this.Method1Async(forServer).Wait();
    });

    Debug.WriteLine("Finished");
}

private async Task Method1Async(string server)
{
    await this.Method2Async(server);
}

private async Task Method2Async(string server)
{
    Debug.WriteLine("> Method2Async");

    string result = await Task<string>.Run(() =>
    {

        Debug.WriteLine("  Method2Async before return");

        return GetDataFromServer(server);
    });

    Debug.WriteLine("< Method2Async");
}

private string GetDataFromServer(string server)
{
    // any long time running stuff

    Thread.Sleep(10000);

    return "the server data";
}

注:感谢Theodor Zoulias提到这一点:

根据
Parallel.ForEach
不会等待任务完成,因此
wait
ing在操作中不会做任何事情,并且
IsCompleted
将在所有任务启动后立即设置为true


ForEach
操作的签名更改为
async
,以启用
wait
ing

using System.Threading.Tasks;
using System.Diagnostics;

private void Button_Click(object sender, RoutedEventArgs e)
{
    string[] dummyArray = new string[] { "anyvalue" };

    Parallel.ForEach(dummyArray, async (forDummy) =>
    {
        await this.Method1Async();
    });

    Debug.WriteLine("Finished");
}

private async Task Method1Async()
{
    await this.Method2Async();
}

private async Task Method2Async()
{
    Debug.WriteLine("> Method2Async");

    string result = await Task<string>.Run(() =>
    {
        Debug.WriteLine("  Method2Async before return");
        return "anydata"; // this return sometimes does not "come back" ...
    });

    // ... so this code is never reached
    Debug.WriteLine("< Method2Async" + result);
}

编辑: 如果要限制并行任务的数量,可以执行以下操作:


另一种选择是使用显然非常流行的

wait
able
foreach
构造可以通过迭代任何
IEnumerable
IAsyncnumerable来实现。

using Dasync.Collections;

string[] servers = new string[] { "10.17.100.1", "10.17.100.10", "10.17.100.20" };

await servers.ParallelForEachAsync<string>(async forServer =>
{
    await this.Method1Async(forServer);

}, maxDegreeOfParallelism: 10);

不要在任务上使用
.Wait()
。什么叫“等待并行.ForEach的完成”?这是一个同步调用,直到所有并行任务完成后才会返回。IIRC,await告诉编译器和运行时“重新洗牌,使函数的其余部分成为回调”。Wait()只是一个函数调用。等待等于或略高于括号。很容易让他们混淆。请更新您的问题,没有
anydata
,您可能会发现这很有趣:。问题中还有一个嵌套的
Parallel.ForEach
,看起来像是抄本错误。@marsh wiggle您可以改为,创建一个循环并检查ParallelLoopResult.IsCompleted,但对于本例来说,这是一个过分的做法。@marsh wiggle,您在
想要的输出中说过,您希望在第一个循环完成后调用第二个循环。对吗?@marsh wiggle我添加了另一个更新。我希望它有助于并行.ForEach
。通过的lambda是@marsh wiggle请再次查看更新的答案和上面的评论。似乎平行foreach有一个局限性。
using System.Threading.Tasks;
using System.Diagnostics;

private void Button_Click(object sender, RoutedEventArgs e)
{
    string[] dummyArray = new string[] { "anyvalue" };

    Parallel.ForEach(dummyArray, async (forDummy) =>
    {
        await this.Method1Async();
    });

    Debug.WriteLine("Finished");
}

private async Task Method1Async()
{
    await this.Method2Async();
}

private async Task Method2Async()
{
    Debug.WriteLine("> Method2Async");

    string result = await Task<string>.Run(() =>
    {
        Debug.WriteLine("  Method2Async before return");
        return "anydata"; // this return sometimes does not "come back" ...
    });

    // ... so this code is never reached
    Debug.WriteLine("< Method2Async" + result);
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
    string[] dummyArray = new string[] { "anyvalue" };

    Task[] tasks = dummyArray.Select(async x => await Method1Async()).ToArray();
    await Task.WhenAll(tasks);
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
    string[] dummyArray = new string[] { "anyvalue" };

    Task[] tasks = dummyArray.Select(x => Method1Async()).ToArray();
    await Task.WhenAll(tasks);
}
    public async Task Button_Click()
    {
        string[] servers = new string[] { "1", "2", "3", "4", "5" };

        var maxParallel = 3;
        var throttler = new SemaphoreSlim(initialCount: maxParallel);
        var tasks = servers.Select(async server =>
        {
            try
            {
                await throttler.WaitAsync();
                await Method1Async(server);
            }
            finally
            {
                throttler.Release();
            }
        });
        await Task.WhenAll(tasks);

        Console.WriteLine("Finished");
    }
using Dasync.Collections;

string[] servers = new string[] { "10.17.100.1", "10.17.100.10", "10.17.100.20" };

await servers.ParallelForEachAsync<string>(async forServer =>
{
    await this.Method1Async(forServer);

}, maxDegreeOfParallelism: 10);
using Dasync.Collections;

string[] servers = new string[] { "10.17.100.1", "10.17.100.10", "10.17.100.20" };

ConcurrentBag<string> bag = new ConcurrentBag<string>();

await severs.ParallelForEachAsync<string>(async forServer =>
{
    string response = await this.Method1Async(forServer);

    bag.Add(response);

}, maxDegreeOfParallelism: 10); 

foreach(string forBagItem in bag)
{
    // evaluate the results
}