2011-07-12 104 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 

}