线程切换

时间:2011-07-12 19:03:55

标签: multithreading c#-2.0

如何在调用异步函数的同一线程中执行回调方法。 调用者线程可能不是UI线程...但UI不应该挂起..

谢谢&问候, 迪内希

3 个答案:

答案 0 :(得分:0)

使用BackgroundWorker。回调将在拥有的线程上。

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

答案 1 :(得分:0)

没有灵丹妙药可以允许一个线程将委托执行到另一个线程上。目标线程必须经过特殊构造才能实现。在UI线程的情况下,有一个消息泵,用于调度和处理消息。此消息泵可用于通过ISynchronizeInvoke接口执行编组操作。

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下载的一部分提供。如果您无法使用此数据结构,那么这个已经很困难的代码变得更加困难。

答案 2 :(得分:0)

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

每当你想把这个类的执行编组到创建它的线程中时,只需调用它的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

}