【发布时间】:2021-10-22 08:24:45
【问题描述】:
我已经阅读了许多以前关于 .NET 中的多线程的帖子,并尝试了多种方法,但我很难让我的特定案例和 c#.NET 代码正常运行。我的代码旨在在后台运行长时间运行的进程,并将正在进行的进度报告到我的 Windows 窗体 UI 中的日志显示和同一窗体上的进度条控件中。目前它可以运行,但是 UI 线程被锁定,并且在后台进程完成之前不会将消息推送到 UI。
为了简洁起见,我在这里总结一下目前的框架……
用户界面表单
在初始化过程中,我们将 Synchronization 上下文设置如下 ...
// reference to synchronization context within the UI initialization
Form_StepProcessor.SynchronizationContext =
(WindowsFormsSynchronizationContext)System.Threading.SynchronizationContext.Current;
表单还包含一个自定义事件处理程序,它将 Post 回调包装到同步上下文 ...
// attach asynchronous result processing handler within the UI...
try
{
AbsNhdPlusDotNetControllerAsync.ProcessResult -= this.OnProcessDotNetAsyncResult;
}
catch
{ // ignore
}
finally
{
AbsNhdPlusDotNetControllerAsync.ProcessResult += this.OnProcessDotNetAsyncResult;
}
// process result event declared within the controller
public delegate void ProcessResultEvent(object sender, ProcessResultEventArgs e);
public static event ProcessResultEvent ProcessResult;
public class ProcessResultEventArgs : EventArgs
{
public Exception Exception { get; set; }
public enpls_ProcessStatus Status { get; set; } = enpls_ProcessStatus.Failure;
public string Message { get; set; } = string.Empty;
public int PercentComplete { get; set; } = 0;
}
// event handler within the UI to push messaging to log control and increment progress bar
private void OnProcessDotNetAsyncResult(object sender, AbsNhdPlusDotNetControllerAsync.ProcessResultEventArgs e)
{
Form_StepProcessor.SynchronizationContext.Post(new SendOrPostCallback(x =>
{
// DO STUFF (i.e. post messages to display, increment progress bar, etc.)...
}), e);
}
后台线程
为了启动后台进程,我们使用了利用 .ConfigureAwait(false) 的 async-await 调用,该调用深入到控制器的“执行”任务方法。此方法包括几个“InvokeMessage”调用,它们调用上面的 UI 处理程序附加到的事件...
// call internal to the controller which immediately calls the "Execute" task method
await this.Execute(pModel_Validation, sTask).ConfigureAwait(false);
protected override Task Execute(iNhdPlusBaseModel pModel, string sTask)
{
// set the synchronization context (this didn't seem to do anything)
SyncrhonizationContext.SetSynchronizationContext(Form_StepProcessor.SynchronizationContext);
// do stuff ...
// invoke log message ...
this.InvokeMessage("here's a sample log message!", 10);
// do stuff ...
// invoke log message ...
this.InvokeMessage("here's a second sample log message!", 20);
// etc.
return Task.CompletedTask;
}
public void InvokeMessage(string sMessage, int iPercentComplete)
{
var pDateTime = DateTime.Now.ToLocalTime();
var sHour = pDateTime.Hour.ToString("D2");
var sMinute = pDateTime.Minute.ToString("D2");
var sSecond = pDateTime.Second.ToString("D2");
var pArgs = new ProcessResultEventArgs
{
Status = enpls_ProcessStatus.Message,
Message = $"{sHour}:{sMinute}:{sSecond}->{sMessage}",
PercentComplete = iPercentComplete
};
AbsNhdPlusDotNetControllerAsync.ProcessResult?.Invoke(this, pArgs);
}
我对此束手无策。多年来,异步等待和多线程实现的各种实践发生了巨大变化,令人困惑。解开不同的实现以查看适用于我的案例的方法一直是我苦苦挣扎的地方,我觉得我做不到了。我非常感谢任何可以为我指明正确方向的建设性帮助,这种方法可以防止我的 UI 锁定并防止我的消息在没有及时推送到 UI 的情况下缓存。
【问题讨论】:
-
只使用以下之一:(异步和任务)或(BackgroundWorker 和事件)或(线程和 EventWaitHandles)。不要尝试混合使用异步和事件等技术,否则您的程序会变得令人难以置信的混乱。
-
@DourHighArch 谢谢你的警告。截至目前,我相信代码中有足够的分离,这些方法不会混合产生不利影响。但是注意事项很好。
标签: c# .net multithreading async-await synchronizationcontext