【问题标题】:Call a method only after jobs on other threads have terminated?仅在其他线程上的作业终止后才调用方法?
【发布时间】:2015-04-19 14:05:45
【问题描述】:

在我的应用程序中,我有两种从 csv 加载数据的方法。这些方法是loadData()loadOtherData()。我想并行运行它们,所以我有两个线程来运行它们。

loadData() 方法也填充一个数据网格视图,而loadOtherData() 方法加载的数据存储在字典myDictionary 中。

数据加载完成后(所以两个线程中运行的方法都完成了)我想调用另一个方法updateGrids(),它以myDictionary为参数。

要使updateGrids() 正常运行,loadData()loadOtherData() 必须已成功运行,否则该方法没有可处理的数据。

只有当其他方法在其他线程中终止时,我才能调用方法updateGrids()

结构如下:

    private void loadData_Click_1(object sender, EventArgs e)
    {
        ThreadStart thread1Start = new ThreadStart(loadData); // load data and fill the first datagridview
        ThreadStart thread2Start = new ThreadStart(loadOtherData); // load data and fill myDictionary
        Thread t1 = new Thread(thread1Start);
        Thread t2 = new Thread(thread2Start);
        t1.Start(); 
        t2.Start();

        updateGrids(myDictionary); // call this method to update remaining datagridviews
    }


    private void updateGrids(Dictionary<string, double> myDictionary)
    {
        // update the remaining datagridviews by using 
        // information in the first datagridview and in myDictionary
    }

但如果我运行它,我会在updateGrids() 时收到错误,因为它没有可使用的数据。如何轻松更改代码以使其正常工作?

【问题讨论】:

  • 所有工作线程都应该提供检查它们是否完成工作的可能性(简单的计数器就可以了)。在他们的工作结束时,他们应该调用一个方法来检查所有线程工作是否完成并调用updateGrid()
  • 您使用的是什么版本的 .Net?这会影响答案。
  • 谢谢,现在添加到标题中。我正在使用 4.5
  • 我从标题中删除了它;标签是此类细节的首选位置。

标签: c# multithreading winforms asynchronous .net-4.5


【解决方案1】:

启动线程后,您希望等到它们完成。所以添加这几行代码:

t1.Join();
t2.Join();

Join 将导致当前线程休眠,直到正在加入的线程完成。

基于 cmets,我假设这是 WinForms。在这种情况下,Join 将阻塞 UI 线程。您可以创建第三个线程来加入其他两个线程,然后调用 Invoke 来调用您的更新方法。

由于您使用的是 .Net 4.5,因此您可以使用 Async / Await;将async 修饰符添加到您的单击方法事件处理程序中,并直接使用任务而不是线程和await 它们。其他答案涵盖了这一点,您也可以查看blog 了解其他详细信息。

【讨论】:

  • 这似乎没有按预期工作。我试过了,但应用程序没有响应,我需要停止程序。
  • @Sinatr “创建第三个线程,它将加入其他两个线程,然后调用 Invoke”不是一个完整的解决方案吗?如果他这样做,它将按预期工作。还是你认为我应该为他写代码?
【解决方案2】:

您可以使用async/await 来等待这两个任务而不会阻塞 UI

async private void loadData_Click_1(object sender, EventArgs e)
{
    await Task.WhenAll(Task.Run(() => loadData()), 
                       Task.Run(() => loadOtherData()));

    updateGrids(myDictionary); 
}

【讨论】:

  • 是否需要将 loadData() 和 loadOtherData( 声明为异步才能并行运行?
  • @mickG 不,我假设他们是void xxx()
  • 谢谢,这按预期工作。这是实现这一目标的最佳方式吗?
  • @mickG “最佳方式” 可能非常主观。但我想是的。
  • 我明白了,你能确认这不会在旧的 .NET 版本上运行吗?
【解决方案3】:

您可以直接停止使用 ThreadStart 对象并改用任务 - 让它们从您的 loadData 和 loadOtherData 方法返回。

您有两种选择:

如果您使用的是 .NET 4.0,请使用 Task.WaitAll

或(更可取)....

在 .NET 4.5 及更高版本中使 loadData 和 loadOtherData 异步(或在 .NET 4.0 中使用异步 BCL 包)。当您调用方法时,您可以缓存它们返回的 Task 对象并等待对 Task.WhenAll 的调用

public async Task LoadData(){ 

}

public async Task LoadOtherData() {

}

private async void loadData_Click_1(object sender, EventArgs e)
{
    var loadDataTask = LoadData();
    var loadOtherDataTask = LoadOtherData();
    await Task.WhenAll(loadDataTask, loadOtherDataTask);

    updateGrids(myDictionary);
} 

【讨论】:

  • 我没有看到反对票,但是由于 OP 没有使用 async / await 你没有完全回答这个问题。建议替代方案很好,但 OP 可能对 Thread 更满意,您也应该回答如何使用它。
  • 反对票已被删除。 OP 正在使用 .NET 4.5。
  • 我不明白他使用 .Net 4.5 的重要性。他的问题是如何让这个与 Thread 一起工作,所以你应该回答那个问题。如果你愿意,建议 async / await ,但它并不像使用 Thread 有什么问题(它有很多关于它的信息,因为它的存在时间更长)。
  • 我不确定我是否真的理解你的意思。任务隐式使用 CLR 中的线程。尽管他最初的问题包含一个使用 Thread 对象的 sn-p,但他并没有说他受此限制。在 .NET 4.5 中使用 async/await 和 Tasks 比在本机使用 Thread 对象更传统,也可以让他更轻松地处理异常情况。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-31
  • 1970-01-01
  • 2019-07-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多