连续运行多个任务

时间:2018-06-28 11:32:48

标签: c# multithreading task

我发现一些奇怪的行为在for循环中运行任务并等待所有任务。如下所述,代码的作用是为一定数量的任务启动一个“循环”,每个任务创建一个“ ToDo”项目,每个任务都有一个延续,该延续将每个创建的任务分配给一个人,最后将它们分配给listBox调用以便调用UI线程。这工作正常,但我没有在listBox中获得预期的数据。我不希望它们被订购,但我确实希望它们可以配对,例如:

人物8,待办事项8 第5人,待办事项5 等等...

它们当然应该只出现在listBox中!但是,相反,我得到了奇怪的输出(每次运行输出都不相同),下面是一些运行代码的示例:

enter image description here enter image description here

这是代码:

private async void buttonCreateToDo_Click(object sender, EventArgs e){
    await CreateToDoAsync();
}

private async Task CreateToDoAsync(){
    List<Task> taskList = new List<Task>();
    for (int i = 1; i < 10; i++){
        var task = Task.Run(() => CreateToDo(i));
        Task continuation = task.ContinueWith((antecedent) => Invoke(new AssignTaskDelegate(AssignTask), (new Person() {
            Name = $"Person_{i}",
            ToDoForPerson = antecedent.Result
        })));
        taskList.Add(task);

    }
    await Task.WhenAll(taskList.ToArray());
}

private ToDo CreateToDo(int toDoId) {
    return new ToDo(){
        Id = toDoId,
        Description = $"Todo {toDoId}"
    };
}

private void AssignTask(Person person){
    listBoxToDo.Items.Add($"{person.Name}, {person.ToDoForPerson.Description}");
}

2 个答案:

答案 0 :(得分:2)

您的问题是for循环的运行速度比任务的创建快得多,因此到任务运行变量i的时间到达循环结束时为止。

要解决此问题,您需要在循环内复制i并使用它。

尝试以下代码:

private async Task CreateToDoAsync()
{
    List<Task> taskList = new List<Task>();
    for (int i = 1; i < 10; i++)
    {
        var local_i = i;
        var task = Task.Run(() => CreateToDo(local_i));
        Task continuation = task.ContinueWith((antecedent) => Invoke(new AssignTaskDelegate(AssignTask), (new Person()
        {
            Name = $"Person_{local_i}",
            ToDoForPerson = antecedent.Result
        })));
        taskList.Add(task);
    }
    await Task.WhenAll(taskList.ToArray());
}

现在,我会优先使用Microsoft的Reactive Framework(NuGet“ System.Reactive”)来完成这项工作。您的代码如下所示:

private async Task CreateToDoAsync()
{
    var query =
        from i in Observable.Range(1, 9)
        from t in Observable.Start(() => CreateToDo(i))
        select new Person() { Name = $"Person_{i}", ToDoForPerson = t };

    await query.ObserveOn(listBoxToDo).Do(x => AssignTask(x));
}

完成。就是这样。

当我运行代码(将AssignTask输出到控制台)时,我得到了:

Person_1, Todo 1
Person_2, Todo 2
Person_3, Todo 3
Person_6, Todo 6
Person_7, Todo 7
Person_4, Todo 4
Person_5, Todo 5
Person_8, Todo 8
Person_9, Todo 9

.ObserveOn(listBoxToDo)适用于Windows窗体以编组回UI线程。

答案 1 :(得分:-2)

您正在任务内部的循环中使用变量“ i”。您的任务开始时“ i”可能已更改。