c# - 是否可以将事件处理程序放在与调用者不同的线程上?

标签 c# multithreading events delegates

假设我有一个名为 Tasking 的组件(我无法修改),它公开了一个方法“DoTask”,该方法执行一些可能冗长的计算并通过事件 TaskCompleted 返回结果。通常这是在用户获得结果后关闭的 Windows 窗体中调用的。

在我的特定场景中,我需要将一些数据(数据库记录)与 TaskCompleted 中返回的数据相关联,并使用它来更新数据库记录。

我研究了使用 AutoResetEvent 来通知事件何时被处理。问题是 AutoResetEvent.WaitOne() 将阻塞并且永远不会调用事件处理程序。通常 AutoResetEvents 被称为一个单独的线程,所以我猜这意味着事件处理程序与调用的方法位于同一线程上。

本质上,我想将异步调用(结果通过事件返回)转换为同步调用(即从另一个类调用 DoSyncTask),方法是阻塞直到事件被处理并且结果放置在两者都可以访问的位置事件处理程序和调用启动异步调用的方法的方法。

public class SyncTask
{
    TaskCompletedEventArgs data;
    AutoResetEvent taskDone;

public SyncTask()
{
    taskDone = new AutoResetEvent(false);
}

public string DoSyncTask(int latitude, int longitude)
{
    Task t = new Task();
    t.Completed = new TaskCompletedEventHandler(TaskCompleted);
    t.DoTask(latitude, longitude);
    taskDone.WaitOne(); // but something more like Application.DoEvents(); in WinForms.
    taskDone.Reset();
    return data.Street;
}

private void TaskCompleted(object sender, TaskCompletedEventArgs e)
{
    data = e;
    taskDone.Set(); //or some other mechanism to signal to DoSyncTask that the work is complete.
}
}

In a Windows App the following works correctly.

public class SyncTask
{
    TaskCompletedEventArgs data;

public SyncTask()
{
    taskDone = new AutoResetEvent(false);
}

public string DoSyncTask(int latitude, int longitude)
{
    Task t = new Task();
    t.Completed = new TaskCompletedEventHandler(TaskCompleted);
    t.DoTask(latitude, longitude);
    while (data == null) Application.DoEvents();

    return data.Street;
}

private void TaskCompleted(object sender, TaskCompletedEventArgs e)
{
    data = e;
}
}

我只需要在窗口服务中复制该行为,其中不调用 Application.Run 并且 ApplicationContext 对象不可用。

最佳答案

我最近在线程中进行异步调用和事件并将它们返回到主线程时遇到了一些麻烦。

我用了SynchronizationContext跟踪事情。下面的(伪)代码显示了目前对我有用的东西。

SynchronizationContext context;

void start()
{
    //First store the current context
    //to call back to it later
    context = SynchronizationContext.Current; 

    //Start a thread and make it call
    //the async method, for example: 
    Proxy.BeginCodeLookup(aVariable, 
                    new AsyncCallback(LookupResult), 
                    AsyncState);
    //Now continue with what you were doing 
    //and let the lookup finish
}

void LookupResult(IAsyncResult result)
{
    //when the async function is finished
    //this method is called. It's on
    //the same thread as the the caller,
    //BeginCodeLookup in this case.
    result.AsyncWaitHandle.WaitOne();
    var LookupResult= Proxy.EndCodeLookup(result);
    //The SynchronizationContext.Send method
    //performs a callback to the thread of the 
    //context, in this case the main thread
    context.Send(new SendOrPostCallback(OnLookupCompleted),
                 result.AsyncState);                         
}

void OnLookupCompleted(object state)
{
    //now this code will be executed on the 
    //main thread.
}

我希望这对我有所帮助,因为它解决了我的问题。

关于c# - 是否可以将事件处理程序放在与调用者不同的线程上?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/486393/

相关文章:

java - 内存一致性属性解释

c# - 如何在 MonoTouch 中使用 async/await 忽略未观察到的异常?

c# - 合格双倍显示不正常

C++11 简单生产者消费者多线程

c++ - 在不锁定的情况下在线程之间复制 std::vector

c# - 后台 worker 订阅事件

events - Safari for Mac 桌面版 : HTML 5 audio "ended" event doesn't fire problem

.net - 多线程委托(delegate)/事件

c# - Scaffold-DbContext 在.net core 中抛出错误 "Could not find assembly"

c# - Azure Keyvault - "Operation "列表“保管库策略不允许”,但已检查所有权限