【问题标题】:Invoking method on thread which created object在创建对象的线程上调用方法
【发布时间】:2010-12-22 19:11:23
【问题描述】:

假设我在线程 T 上创建了一个对象 O。我怎样才能从对象 O 内部获取线程 T 并调用该线程上的方法?这样,创建对象的表单就不需要这样做了:

    private void ChangeProgress(int value)
    {
        progressBar1.Value = value;
    }

    void FD_ProgressChanged(object sender, DownloadEventArgs e)
    {
        if (InvokeRequired)
        {
            Invoke(new Action<int>(ChangeProgress), new object[] { e.PercentDone });
        }
        else ChangeProgress(e.PercentDone);
    }

这很丑陋,并且要求使用该对象的人要么找出在创建对象的同一线程上引发了哪些事件,哪些不是,然后在没有的事件上添加 if(InvokeRequired)...else 代码,或者只是添加每个事件处理程序上的代码。我认为如果对象本身负责在正确的线程上调用事件会更优雅。这可能吗?

【问题讨论】:

  • 其实我会听汉斯的话,用BackgroundWorker重写课程。我可以环绕ReportProgress 来通知我想要的任何东西。

标签: c# multithreading events


【解决方案1】:

使用 BackgroundWorker 类。它负责这一切。注意 ReportProgress 事件。

【讨论】:

    【解决方案2】:

    您将不得不像自己一样跟踪它

    class Foo {
        private readonly Thread creatingThread;
    
        public Foo() {
            this.creatingThread = Thread.CurrentThread;
        }
    }
    

    如果你不这样做,就没有办法知道。但你这样做的事实是一种气味。考虑使用BackgroundWorker

    【讨论】:

      【解决方案3】:

      您需要考虑以下几点:

      • 您将需要在 Object O 中保留创建它的线程的引用。可能在使用 Thread.Current 静态属性的构造函数中。
      • 该线程需要有一个与之关联的SynchronizationContext。 (一般来说,UI 线程都有。而且要为您创建的自定义线程创建一个并不容易。)
      • 要在该线程上调用方法,您需要在该线程的SynchronizationContext 上使用Send()Post() 方法。

      【讨论】:

      • 如何获得SynchronizationContext?我知道这个线程是一个 UI 线程,所以它必须有一个,但是你是怎么得到它的呢?
      【解决方案4】:

      http://www.codeproject.com/KB/threads/invoke_other_way.aspx找到了一个不错的解决方案

      这是我的通用版本:

          private void RaiseEventAsync(Delegate handler, object e)
          {
              if (null != handler)
              {
                  List<Delegate> invocationList = handler.GetInvocationList().ToList();
      
                  foreach (Delegate singleCast in invocationList)
                  {
                      System.ComponentModel.ISynchronizeInvoke syncInvoke =
                                 singleCast.Target as System.ComponentModel.ISynchronizeInvoke;
                      try
                      {
                          if ((null != syncInvoke) && (syncInvoke.InvokeRequired))
                              syncInvoke.Invoke(singleCast,
                                            new object[] { this, e });
                          else
                              singleCast.Method.Invoke(singleCast.Target, new object[] { this, e });
                      }
                      catch
                      { }
                  }
              }
          }
      

      这就是你将如何使用它:

          protected void OnProgressChanged(DownloadEventArgs e)
          {
              RaiseEventAsync(ProgressChanged, e);
          }
      

      这可以解决我的问题,而无需使用并非总是需要的 BackgroundWorker(就像在我的情况下,我正在对已经使用不同线程对象的类进行子类化)。

      【讨论】:

      • 太棒了!这样,您的代码将永远与 WinForms 绑定!或者...您可以使用任务并行库并保持 UI 独立性。
      猜你喜欢
      • 1970-01-01
      • 2018-03-28
      • 2012-09-11
      • 2012-11-02
      • 1970-01-01
      • 2010-10-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多