【问题标题】:make multiple calls to same async tasks with different data c#使用不同的数据c#多次调用相同的异步任务
【发布时间】:2017-09-13 12:22:23
【问题描述】:

我有一个包含一些文档的列表(为简单起见字符串)。现在该列表正在慢慢填充。我想要做的是当列表的大小达到 20 时,我想调用另一个函数,该函数将异步打印这些字符串而不停止 main 方法。经过大量搜索,我设法将这段代码放在一起

public void DoStuff()
{
    Class1 p = new Class1();
    List<string> list = new List<string> { };
    var TList = new List<Task>();
    int i = 0;
    while (i < 90)
    {
        list.Add(i.ToString());
        if (list.Count == 20)
        {
            Console.WriteLine("List contents when calling: " + list[0]);
            TList.Add(Task.Run(() => publishDoc(list)));
            list.Clear();
        }
        i++;
    }
    if (list.Count != 0)
    {
        TList.Add(Task.Run(() => publishDoc(list)));
    }
    Task.WhenAll(TList).Wait();
    Console.WriteLine("Done DoStuff");
}

public async Task publishDoc(List<string> docs)
{
    Console.WriteLine(iter++ + " " + docs[0]);
    await Task.Run(() => Thread.Sleep(1000));
    foreach (var val in docs)
    {
        Console.Write(val + " ");
    }
    Console.WriteLine();
}

这是我得到的输出

List contents when calling: 0
List contents when calling: 20
List contents when calling: 40
List contents when calling: 60
1 80
3 80
0 80
2 80
4 80
80 80 80 80 81 82 83 84 85 86 87 88 89
81 82 83 84 85 86 87 88 89
81 82 83 84 85 86 87 88 89
81 82 83 84 85 86 87 88 89
80 81 82 83 84 85 86 87 88 89
Done DoStuff
Done Main

我无法弄清楚为什么它只打印最后传递的数据,即为什么传递的列表被覆盖。 现在如果我这样做

public void DoStuff()
{
    Program2 p = new Program2();
    List<string> list = new List<string> { };
    int i = 0;
    while (i < 90)
    {
        list.Add(i.ToString());
        if (list.Count == 20)
        {
            Console.WriteLine("List contents when calling: " + list[0]);
            var tasks = publishDoc(list);
            if (tasks.Result == "Done")
            {
                Console.WriteLine("Done " + list[0]);
            }
            list.Clear();
        }
        i++;
    }
    if (list.Count != 0)
    {
        var tasks = publishDoc(list);
        if (tasks.Result == "Done")
        {
            Console.WriteLine("Done " + list[0]);
        }
    }
    Console.WriteLine("Done DoStuff");
}

public async Task<string> publishDoc(List<string> docs)
{
    Console.WriteLine(iter++ + " " + docs[0]);
    foreach (var val in docs)
    {
        Console.Write(val + " ");
    }
    Console.WriteLine();
    return await Task.Run(() => "Done");
}

我得到这个输出

List contents when calling: 0
0 0
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Done 0
List contents when calling: 20
1 20
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
Done 20
List contents when calling: 40
2 40
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
Done 40
List contents when calling: 60
3 60
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
Done 60
4 80
80 81 82 83 84 85 86 87 88 89
Done 80
Done DoStuff
Done Main

这给出了正确的输出,但是我不希望它同步进行。请提前帮助和感谢。

【问题讨论】:

  • 1.忘记你的第二种方法。 2. list.Clear(); - 你认为这会做什么?它会清除您刚刚传递给任务的引用的列表。
  • 您的第一个列表几乎是正确的,但是传递了对同一列表实例的引用。而不是list.Clear(); 使用list = new List&lt;string&gt;();
  • 这是在 C# 中使用线程时的常见问题。想象一下,您的 Main Task 已经通过了 while 方法,因此您在 Task.Run() 中的 lambda 只会得到 80

标签: c# asynchronous async-await


【解决方案1】:

你必须这样做,因为在循环中它每次都指向相同的列表实例,这可能会导致问题,所以我建议你在将它传递给函数 publishDoc 之前创建列表的副本

while (i < 90)
    {
        list.Add(i.ToString());
        if (list.Count == 20)
        {
            List<string> copy = list.ToList();
            Console.WriteLine("List contents when calling: " + copy[0]);
            TList.Add(Task.Run(() => publishDoc(copy)));
            list.Clear();
        }
        i++;
    }

也参考这个答案:Starting Tasks In foreach Loop Uses Value of Last Item

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-06
    • 1970-01-01
    • 2018-07-20
    • 1970-01-01
    相关资源
    最近更新 更多