6

我怎么能在被调用onCompleteCallBack的同一个线程上调用该方法SomeAsyncMethod

public void SomeAsycMethod ( Action<object> onCompleteCallBack )
{
    // get the current thread 
    /* var ThisThread = Thread.CurrentThread. */

    Task.Factory.StartNew( () =>
    {
        Thread.Sleep( 1000 );// do some work;

        // lastly call the onCompleteCallBack on 'ThisThread'
        onCompleteCallBack( "some result" );

        // I am looking for something like:
        /* ThisThread.Invoke("some result"); */
    });
}
4

2 回答 2

6

虽然你不能保证你的回调会在同一个线程上被调用,但你可以保证它会在同一个同步上下文中被调用(假设原始调用中存在一个)。

public void SomeAsycMethod ( Action<object> onCompleteCallBack )
{
    // get the current context
    var context = SynchronizationContext.Current;

    Task.Factory.StartNew( () =>
    {
        Thread.Sleep( 1000 );// do some work;

        // lastly call the onCompleteCallBack on 'ThisThread'
        onCompleteCallBack( "some result" );

        // I am looking for something like:
        context.Post(s => onCompleteCallBack ("some result"), null); 
    });
}

例如,在 Windows 窗体或 WPF 程序中,以上将确保在 GUI 线程上调用回调(相应地通过消息循环或调度程序)。对于 ASP.NET 上下文也是如此。

话虽如此,我同意 Justin Harvey 的观点,即返回 aTask<T>可能是一个更好的设计。

于 2012-11-28T16:31:09.837 回答
4

实际上,如果您使用的是基于任务的异步编程,我建议您重构代码以返回Task<T>并让您的客户端本身能够决定在什么上下文中调用回调方法(并促进未来向 C# 5.0 的迁移;):

public Task<string> SomeMethodAsync()
{
   return Task.Factory.StartNew(() => "some result");
}

如果您确定要从 UI 线程调用此方法,则可以使用以下方法:

var task = SomeMethodAsync();
task.ContinueWith(t => textBox.Text = t.Result, TaskScheduler.FromSynchronizationContext);

这种方法更好,因为它提供了更清晰的关注点分离,并提供了在任何上下文中使用异步方法而不依赖于同步上下文的能力。一些客户端可以从 UI 线程调用此方法(并且在这种情况下TaskScheduler.FromSynchronizationContext会按预期运行 - 您的“继续”将在 UI 线程中调用),其中一些客户端也可以从非 UI 线程使用您的方法,而无需像处理这样的要求导致启动异步操作的同一线程。

Task<T>是一个完美的类,它将异步操作表示为第一类对象,它不仅有助于获得更多的声明性代码,而且更清晰、易于阅读和易于测试(您可以轻松地模拟此方法并返回“假”任务对象)。

于 2012-11-28T18:18:52.287 回答