2011-07-12 73 views
1

如何执行在同一线程回调方法,它调用异步函数。 程序线程可能不是UI线程...但是UI不应该挂..线程切换

感谢&问候, 迪内希

回答

0

没有灵丹妙药,这将允许一个线程启动委托执行到另一个线程。目标线程必须特别构造以允许这样做。在UI线程的情况下,有一个消息泵来分派和处理消息。该消息泵可用于通过接口执行编组操作。

ISynchronizeInvoke target = someForm; // where someForm is a Form or Control 
target.Invoke(
    (Action)(() => 
    { 
    MessageBox.Show("I am on the target thread"); 
    }), null); 

在你的情况下,线程调用异步函数必须具有某种内置到它的生产者 - 消费者的机制来获得一个回调到该线程异步执行其已经被指示从辅助线程这样做后, 。不幸的是,这不是一个可以解决的小问题。

这里是你可以创建一个线程,可以接受委托执行的一个途径。

public class SynchronizeInvokeThread : ISynchronizeInvoke 
{ 
    private Thread m_Thread; 
    private BlockingCollection<WorkItem> m_Collection = new BlockingCollection<WorkItem>(); 

    public SynchronizeInvokeThread() 
    { 
     m_Thread = new Thread(
      () => 
      { 
       SynchronizationContext.SetSynchronizationContext(new MySynchronizationContext(this)); 
       while (true) 
       { 
        WorkItem wi = m_Collection.Take(); 
        wi.Complete(wi.Method.DynamicInvoke(wi.Args)); 
       } 
      }); 
     m_Thread.Start(); 
    } 

    public IAsyncResult BeginInvoke(Delegate method, object[] args) 
    { 
     var wi = new WorkItem(method, args); 
     m_Collection.Add(wi); 
     return wi; 
    } 

    public object EndInvoke(IAsyncResult result) 
    { 
     var wi = (WorkItem)result; 
     wi.AsyncWaitHandle.WaitOne(); 
     return wi.Result; 
    } 

    public object Invoke(Delegate method, object[] args) 
    { 
     var wi = new WorkItem(method, args); 
     m_Collection.Add(wi); 
     wi.AsyncWaitHandle.WaitOne(); 
     return wi.Result; 
    } 

    public bool InvokeRequired 
    { 
     get { return Thread.CurrentThread != m_Thread; } 
    } 

    private class MySynchronizationContext : SynchronizationContext 
    { 
     private ISynchronizeInvoke m_SynchronizingObject; 

     public MySynchronizationContext(ISynchronizeInvoke synchronizingObject) 
     { 
      m_SynchronizingObject = synchronizingObject; 
     } 

     public override void Post(SendOrPostCallback d, object state) 
     { 
      m_SynchronizingObject.BeginInvoke(d, new object[] { state }); 
     } 

     public override void Send(SendOrPostCallback d, object state) 
     { 
      m_SynchronizingObject.Invoke(d, new object[] { state }); 
     } 
    } 

    private class WorkItem : IAsyncResult 
    { 
     private Delegate m_Method; 
     private object[] m_Args; 
     private object m_Result = null; 
     private ManualResetEvent m_Signal = new ManualResetEvent(false); 

     public WorkItem(Delegate method, object[] args) 
     { 
      m_Method = method; 
      m_Args = args; 
     } 

     public void Complete(object result) 
     { 
      m_Result = result; 
      m_Signal.Set(); 
     } 

     public object Result 
     { 
      get { return m_Result; } 
     } 

     public Delegate Method 
     { 
      get { return m_Method; } 
     } 

     public object[] Args 
     { 
      get { return m_Args; } 
     } 

     public object AsyncState 
     { 
      get { return null; } 
     } 

     public WaitHandle AsyncWaitHandle 
     { 
      get { return m_Signal; } 
     } 

     public bool CompletedSynchronously 
     { 
      get { return false; } 
     } 

     public bool IsCompleted 
     { 
      get { return m_Signal.WaitOne(0); } 
     } 
    } 
} 

它可以像这样使用。

ISynchronizeInvoke target = new SynchronizeInvokeThread(); 
target.Invoke(
    (Action)(() => 
    { 
    Console.WriteLine("I am on the target thread"); 
    SynchronizationContext.Current.Post(
     (state) => 
     { 
     Console.WriteLine("I even have a synchronization context!"); 
     }, null); 
    }), null); 

更新:

每低于BlockingCollection评论仅在.NET 4.0或为Reactive Extensions下载的一部分提供。如果这个数据结构不适合你,那么这个已经很难的代码变得更加困难。

+0

有什么样? – dinesh

+0

我更新了我的答案。 –

+0

@Brain Gideon:使用'CustomThread'不会调用目标线程中的代码。这是因为你不使用'SynchronizationContext' ...来证明它。在Windows窗体上运行一个测试,并用'textBox1.Text =“一些数据”替换'Console.WriteLine';''你将有一个交叉线程异常..另外请注意,您正在使用'ConcurrentCollection',问题标签是C#3.0' –

0

使用一个BackgroundWorker。回调将在自己的线程上。

如果即使在回调之后异步操作仍需要运行,您可以使用WPF System.Windows.Application.Current.Dispatcher.Invoke/BeginInvoke进行多个回调,或者如果可以使用WinForms窗体或控件实例本身并调用Invoke/BeginInvoke。

+0

如果它是一个控制台应用程序,然后的BackgroundWorker的完成事件被称为在不同的线程。在我们呼吁RunAsync fnction .. – dinesh

0

Brian Gideon提到你应该使用ISynchronizeInvoke“System.ComponentModel.ISynchronizeInvoke”。在你希望在另一个线程上封送其线程执行的类上实现它。这里的示例Media类“我实现的一些类与Com对象进行交互,因此它应该在主线程中执行它的方法”;由于类实现它使用System.Threading.SynchronizationContext.Current,因此您可以在WindowsForms中使用它,但不是控制台应用程序,因为System.Threading.SynchronizationContext.Current为空。

无论何时您想将此类的执行编组到创建它的线程时,只需调用其Invoke方法即可。

public abstract class Media : ISynchronizeInvoke 
{ 
     //.... 

     private readonly System.Threading.SynchronizationContext _currentContext = System.Threading.SynchronizationContext.Current; 

     private readonly System.Threading.Thread _mainThread = System.Threading.Thread.CurrentThread; 

     private readonly object _invokeLocker = new object(); 
     //.... 


     #region ISynchronizeInvoke Members 

     public bool InvokeRequired 
     { 
      get 
      { 
       return System.Threading.Thread.CurrentThread.ManagedThreadId != this._mainThread.ManagedThreadId; 
      } 
     } 

     /// <summary> 
     /// This method is not supported! 
     /// </summary> 
     /// <param name="method"></param> 
     /// <param name="args"></param> 
     /// <returns></returns> 
     [Obsolete("This method is not supported!", true)] 
     public IAsyncResult BeginInvoke(Delegate method, object[] args) 
     { 
      throw new NotSupportedException("The method or operation is not implemented."); 
     } 

     /// <summary> 
     /// This method is not supported! 
     /// </summary> 
     /// <param name="method"></param> 
     /// <param name="args"></param> 
     /// <returns></returns> 
     [Obsolete("This method is not supported!", true)] 
     public object EndInvoke(IAsyncResult result) 
     { 
      throw new NotSupportedException("The method or operation is not implemented."); 
     } 

     public object Invoke(Delegate method, object[] args) 
     { 
      if (method == null) 
      { 
       throw new ArgumentNullException("method"); 
      } 

      lock (_invokeLocker) 
      { 
       object objectToGet = null; 

       SendOrPostCallback invoker = new SendOrPostCallback(
       delegate(object data) 
       { 
        objectToGet = method.DynamicInvoke(args); 
       }); 

       _currentContext.Send(new SendOrPostCallback(invoker), method.Target); 

       return objectToGet; 
      } 
     } 

     public object Invoke(Delegate method) 
     { 
      return Invoke(method, null); 
     } 

     #endregion//ISynchronizeInvoke Members 

}