【问题标题】:returning result from an extra thread从额外的线程返回结果
【发布时间】:2012-04-13 10:13:42
【问题描述】:

我想让result(繁重进程的返回结果)在额外线程中执行Heavy Process后可访问,因为它主要是预期不希望在进程UI生效期间, 进程完成后可以玩result

在一些搜索过程中,查看我的文章,我发现有 几种方法 实现这一点,请提供您知道或在类似情况下使用的最佳方法

这是我想以最佳方式处理的样本:

        public ACollection DoProcess(Document docProcess) 
        {
            ACollection result = new ACollection ();

            ThreadStart threadStart = delegate
            {
                result = MyProcess(docProcess);
            };
            var threadProcess = new Thread(threadStart);
            threadProcess.Start();    

            return result ;
}

其他可能的方法可能是 IAsyncResult、BackgroundWorker、使用计时器和检查状态,而不是将结果返回给处理它的方法并将其报告给 UI,以线程安全的方式将其直接发送到我们的 UI 控件。 ..

请在类似案例中给出您自己的意见和样本, 提前致谢

编辑 3:方法 - 基于 Brian 的回答

        LenzCollection myResultCollection = new LenzCollection();       

        TaskScheduler ui = TaskScheduler.FromCurrentSynchronizationContext();

        Task.Factory.StartNew(() =>
          {
              myResultCollection = DoCollect(docProcess);
              //Task.WaitAll();

              return myResultCollection;
          }).ContinueWith((task =>
          {
              myResultCollection = task.Result;
          }), ui);

        return myResultCollection;  

它仍然没有等待,也不会给出想要的结果

【问题讨论】:

  • while (threadProcess.IsAlive) { } 使这个单线程。还有一个 CPU 猪。
  • 几分钟后我得到了编辑,谢谢,但仍然需要知道你建议的可重用方法,thx
  • BackgroundWorker 已经获得了您询问的所有功能。那么为什么要重新发明轮子呢?
  • 为了让某些模块的编码风格与以前保持一致,并以这种方式使它们可重用,也许我错了,但我认为当你想使用 BackgrounndWorker 时,你的可重用性代码将被牺牲,我是对还是错?
  • 这时候对我来说真的很难判断两个答案,我投票给每个参与者,关于选择的答案我会在测试 Tigran 的方法后决定,这对我来说是新的,我也喜欢 Raphael 的回答,因为 Events 是我的朋友,并且实施很好,并且为您提供了所需的灵活性,我应该去并会在 2-3 小时后返回,并会使用建议的方法,看看在这种情况下哪种方法最好,谢谢大家,真的很有帮助....

标签: c# multithreading


【解决方案1】:

在桌面 (WinForms/WPF) 应用程序中,最好和最简单的方法是 BackgroundWorker。它旨在处理这个问题。

【讨论】:

  • 在很多文章中我看到它在大多数类似的问题中都被推荐,但是你知道我想在我使用我制作的模块的方式上拥有更多的权限,这样我应该加入一些单独的部分代码并重塑代码的模块/结构,如果这是必要的并且没有代码可以挑战它,所以我会这样做,无论如何,如果您可以在我的情况下提供任何建议的代码示例,我将非常高兴和感激跨度>
【解决方案2】:

只需实现一个事件,在执行方法之前注册它。并让该方法在完成后引发事件。

例如:

public delegate void MyEventHandler(string result);
public class MyClass
{
  public event MyEventHandler MyEvent;
  private void Event(string result)
  {
    MyEventHandler handler = MyEvent;
    if(handler != null)
      handler(result);
  }

  public void DoSomethingLong()
  {
    //Do something
    Event("Finish");
  }
}

现在你可以像这样运行了

MyClass.Event += MyEventHandler(On_Event);
Thread t = new Thread(MyClass.DoSomethingLong);
t.Start();    

void On_Event(string result)
{
  //Get executed when finished;
}

调用 UI 线程的示例

void On_Event(string result)
{
  if(this.InvokeRequired)
  {
    MyEventHandler handler = new MyEventHandler(On_Event);
    Invoke(handler, new object[] { result });
    return;
  }
  // At this point you can access your UI without having Cross-Threaded operation!
}

【讨论】:

  • 我会检查它并认为它可能是一段可重用的代码,作为一般事件,真的谢谢,它有什么缺点吗?你如何将它与 BackgroundWorker 进行比较?
  • 我会说,您对自己的事件实现更加灵活。您可以随时发起事件,并且可以像这样更新您的 UI 而不会冻结。也许您必须注意一点,如果一个事件被引发,那么您注册事件的方法(在我的示例 On_Event 中)将在方法的线程中执行。所以如果你想更新你的 UI,你必须在你的 UI 线程中调用。我编辑我的帖子并给你一个例子!
  • 请给我一些时间来测试它们,我会采纳你的建议并感谢他们,非常感谢
【解决方案3】:

选项 #1:

使用Task 类和ContinueWith 方法。

TaskScheduler ui = TaskScheduler.FromCurrentSynchronizationContext();

Task.Factory.StartNew(() =>
  {
    var result = MyProcess(docProcess);
    return result.ToString();
  }).ContinueWith((task =>
  {
    textBox1.Text = task.Result;
  }, ui);

您必须记住将正确的同步上下文传递给ContinueWith 方法。你可以看到我是如何使用 ui 变量做到这一点的。

选项 #2:

使用新的asyncawait 关键字。1

private async void SomeButton_Click(object sender, EventArgs args)
{
  textBox1.Text = await Task.Run(() =>
    {
      var result = MyProcess(docProcess);
      return result.ToString();
    });
}

显然,这在视觉上要容易得多,并且可以为您完成所有编组和同步上下文的工作。


1新关键字将在 C# 5.0 中提供,但您现在可以通过 Async CTP 使用它们。 CTP 使用TaskEx 而不是Task

【讨论】:

  • 这是另一个聪明的答案,我喜欢它,如果它基于 C# 5.0 这对我来说更好,现在可以任何方式适用,谢谢你的帖子,我会的几分钟后回来,测试它,如果需要的话会和你讨论,+1 谢谢你的参与 :)
  • 我在发布之前测试了这两种方法,所以它们应该可以工作。请记住,如果通过异步 CTP 使用第二种方法,您需要使用 TaskEx.Run 而不是 Task.Run。不过,这只是 CTP 的事情。
  • 是的,我明白了,我想用它来调整我自己和我的代码,如果需要,我会问你:)
  • 我收到此错误:在等待任务或访问其异常属性时未观察到任务的异常。结果,未观察到的异常被终结器线程重新抛出。
  • 使用了第一种方法,当你在这里时请告诉我,谢谢
【解决方案4】:

如果你使用.NET Framework 4.0 or +,你可以从另一个线程和Task.ContinueWith中的进程运行进程,例如

Process pro = null; 
 Task.Factory.StartNew(() => {
   pro =new Process(..);
   pro.Start(); 
   pro.WaitForExit(); 
}).ContinueWith(..process data recieved from the pro)

编辑

不要使用while(...),而是使用pro.WaitForExit()

希望这会有所帮助。

【讨论】:

  • 在这种情况下这是最好的方法吗?我对此并不熟悉,并认为它可能很有用,因为使用它是提供的代码可以在我的示例中使用?
  • @Sypress: 这是一个最好的还是不应该决定 :) 这是现代的方法和最紧凑型,在这种情况下。不需要声明事件、处理它们或定义 BackgroundWorker。我建议您尝试一下(复制/粘贴),看看它是否符合您的需求。
  • 我明白了,也许很傻,但它确实知道,我无法将我的代码带入/转换它:D,如果“pro”是我上面提到的heavyProcess,我的实例“结果”在哪里我知道这对你来说应该很简单,但你能否提供我的实例,让我更容易基于它移动我的代码,非常感谢这种现代方法;)
  • @Sypress:代码就是您实际看到的代码。 1) 您在另一个线程中运行进程并在 同一线程 上等待其结束,因此 UI(主线程)不会被阻塞。 2)一次进程退出,线程结束,所以写在ContinueWith闭包里面的方法会被执行。所以insied ContinueWith你可以把你使用的方法tio处理你的过程的结果。
  • 请给我一些时间来测试它们,我会采纳你的建议并感谢他们,非常感谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-06
  • 2015-10-14
  • 1970-01-01
  • 2014-02-19
相关资源
最近更新 更多