c# - 使用manualResetEvent的死锁 - c#
问题描述
在 My Sample UI 中,有 3 个按钮。开始按钮,暂停,继续。开始按钮是 async/await 并且会调用一个方法:
private async void btnStart_Click()
{
await Task.Run(() => StartingMethod());
}
我们有一个ManualResetEvent
名为_pauseEvent
. 这是结构StartingMethod()
:
public void StartingMethod()
{
for (int i =0; i < 1000000; i++)
{
_pauseEvent.WaitOne();
// Do Something
}
}
这是暂停/恢复按钮:
private async void btnPause_Click()
{
await Task.Run(() => _pauseEvent.Reset());
}
private async void btnResume_Click()
{
await Task.Run(() => _pauseEvent.Set());
}
但有时它会The handle is invalid
为ManualResetEvent
暂停/恢复通话提供帮助。这似乎是一个竞争条件问题。所以我决定创建一个全局锁对象。
Object _lockObject = new object();
这是新的:
public void StartingMethod()
{
for (int i =0; i < 1000000; i++)
{
lock (_lockObject)
_pauseEvent.WaitOne();
// Do Something
}
}
private async void btnPause_Click()
{
await Task.Run(() => lock (_lockObject) _pauseEvent.Reset());
}
private async void btnResume_Click()
{
await Task.Run(() => lock (_lockObject) _pauseEvent.Set());
}
但看来,在这里,我又将面临锁定对象的死锁问题。我该如何处理这种情况?
解决方案
我很确定使用async void
,Task.Run
会lock
导致死锁。我建议做的是使用 aCancellationToken
和一些中间状态来表示StartingMethod
.
请注意,它不会处理许多StartingMethod
s 的并发执行,您需要使用类似的东西CanExecute
来防止用户连续多次单击开始按钮。
private int _state = 0;
public void StartingMethod(CancellationToken cancellation, bool resetState)
{
if (resetState)
{
_state = 0;
// reset your additional data
}
for (int i = _state; i < 1000000; i++)
{
if (!cancellation.IsCancellationRequested)
{
// Recover saved state
// Do Something
}
else
{
// save intermediate state
// save your additional data
_state = i;
}
}
}
我强烈建议将 try-catch 添加到async
方法中:
private CancellationTokenSource _cts;
private async Task HandleStart(bool resetState)
{
_cts = new CancellationTokenSource();
try
{
await Task.Run(() => StartingMethod(_cts.Token, resetState));
}
catch (Exception e)
{
// log or show some error on UI
}
finally
{
_cts.Dispose();
}
}
private void btnStart_Click()
{
// No need to await because it's a single statement in a method
HandleStart(true);
}
private async void btnPause_Click()
{
_cts.Cancel();
}
private void btnResume_Click()
{
HandleStart(false);
}
推荐阅读
- nginx - NGINX 和 Gatsby:设置 nginx 以路由到 Gatsby 404 页面
- linux-device-driver - 内核空间到 snmp 代理
- parsing - 如何使用 strongGrid 入站 webhook 解析附件值
- go - Go 对 ARM 寄存器 R10 和 R11 的限制
- python - Python中多个类的混淆矩阵
- c - C:消息队列获取错误数据
- php - php mysqli::$insert_id -> 无法正常工作
- reactjs - 在 React 中将表单字段重置为初始状态
- c - 在 C 中编写具有动态内存分配的函数
- assembly - 这如何保证值已在 ARM 中自动更新?