C# 在创建对象的线程上调用方法

C# 在创建对象的线程上调用方法,c#,multithreading,events,C#,Multithreading,Events,假设我在线程T上创建了一个对象O。如何从线程T的对象O内部获取并调用该线程上的方法?。这样,创建对象的窗体就不必执行以下操作: private void ChangeProgress(int value) { progressBar1.Value = value; } void FD_ProgressChanged(object sender, DownloadEventArgs e) { if (InvokeRequir

假设我在线程T上创建了一个对象O。如何从线程T的对象O内部获取并调用该线程上的方法?。这样,创建对象的窗体就不必执行以下操作:

    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);
    }
private void ChangeProgress(int值)
{
progressBar1.值=值;
}
void FD_ProgressChanged(对象发送方,下载事件参数e)
{
如果(需要调用)
{
调用(新操作(ChangeProgress),新对象[]{e.PercentDone});
}
其他变更进度(如完成百分比);
}

这很难看,需要使用该对象的人找出哪些事件在创建该对象的同一线程上引发,哪些没有引发,然后添加
if(InvokeRequired)…或者在没有引发的线程上添加
代码,或者只在每个事件处理程序上添加代码。我认为,如果对象本身负责在正确的线程上调用事件,那么它将更加优雅。这可能吗?

使用BackgroundWorker类。它解决了所有这些问题。请注意ReportProgress事件。

您必须像跟踪一样跟踪它

class Foo {
    private readonly Thread creatingThread;

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

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

有一些事情需要考虑:

  • 您需要在创建它的线程的
    对象O
    中保留一个引用。可能在使用
    Thread.Current
    静态属性的构造函数中
  • 该线程需要有一个与之关联的
    SynchronizationContext
    。(通常,UI线程都有它。为您创建的自定义线程创建一个线程并不容易。)
  • 要在该线程上调用方法,需要在该线程的
    SynchronizationContext
    上使用
    Send()
    Post()
    方法
    • 这是我的通用版本:

          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
                      { }
                  }
              }
          }
      

      这解决了我的问题,而无需使用并非总是需要的
      BackgroundWorker
      (比如在我的案例中,我正在对一个已经使用不同线程对象的类进行子类化)。

      如何获得
      同步上下文
      ?我知道线程是一个UI线程,所以它必须有一个,但你怎么得到它?太好了!这样,您的代码将永远与WinForms绑定!或您可以使用任务并行库并保持UI独立性。实际上,我将听取Hans的意见,并使用
      BackgroundWorker
      重写该类。我可以绕过
      报告进度
      来通知任何我想通知的内容。
          protected void OnProgressChanged(DownloadEventArgs e)
          {
              RaiseEventAsync(ProgressChanged, e);
          }