暂停任务执行

本文关键字:执行 任务 暂停 | 更新日期: 2023-09-27 18:34:31

我有一个长时间运行的任务,其行为类似于事务 - 它涉及许多操作,其中一个操作的成功取决于另一个操作的成功。

class MyTransaction
{
    public void Execute()
    {
        StopServices();
        BackupFiles();
        OverwriteFiles();
        BackupDatabases();
        RunChangeScripts();
        ... and few others        
    }
    public void RollBack() { }
}
class MyTransactionManager
{
    public RunTransactions()
    {
        Task.Factory.StartNew(() => {
            new MyTransaction().Execute();
        });
    }
}

这只是真实应用程序的伪代码,其中系统的不同组件提供了不同的操作。有一个底层GUI(WinForms(使用进度条显示进度,并且无论发生什么都必须保持响应的其他一些东西。事务都运行很长时间,因此在启动任务(使用 TaskCreationOptions(时无需指定它,它总是在新线程中运行。事务的进度使用事件报告回 GUI。

现在,有一个请求,如果事务执行期间某些内容失败,它不会像当前那样立即回滚。他们希望在 GUI 中弹出一个消息框,为用户提供一个选项来决定是回滚还是修复错误并从上一个成功点继续。

所以我需要以某种方式实现阻塞。我以为我可以提出另一个事件,弹出一个消息框并说"嘿,修复它,然后按确定"。并将该 OK 单击事件绑定到我的外部管理器(公共 API(,它可以将请求直接委托给我的事务。阻塞只会主动运行一段时间循环检查一些布尔属性。

现在我认为被动阻止会更好,但我真的不知道该怎么做。你能告诉我吗?

编辑:而且我真的不想使用Thread.Sleep,因为这些错误可能需要不同的时间来修复。取决于错误和修复它的人。

暂停任务执行

阻塞只会主动运行一段时间循环检查一些布尔属性。

这不是阻塞,这叫做忙碌的等待,这是你应该避免的事情。

如果要在两个线程之间进行这样的同步,一种方法是使用 ManualResetEvent

AskUser(); // doesn't block
shouldRollbackEvent.WaitOne();
if (shouldRollback) …

在您的 UI 线程上:

shouldRollback = …;
shouldRollbackEvent.Set();

(这假定代码的两个部分在同一对象中执行。

也许你可以尝试这样的事情

private static Task<bool> WaitTillUserInput()
{
    TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
    uiSynchronizationContext.Post(x =>
    {
        if (MessageBox.Show("Do you want to rollback...?", "Please confirm", MessageBoxButtons.YesNo) == DialogResult.Yes)
        {
            tcs.SetResult(true);
        }
        else
        {
            tcs.SetResult(false);
        }
    }, null);
    return tcs.Task;
}

C# 5.0

public async void Execute()
{
    ...Do something
    //Encountered an error
    var doRollBack = await WaitTillUserInput();
    if(doRollBack)
    {
      //rollback here
    }
}

C# 4.0

public void Execute()
{
    ...Do something
    //Encountered an error
    var doRollBackTask = WaitTillUserInput();
    doRollBackTask.ContinueWith(antecedent =>
    {
        if (antecedent.Result)
        { 
            //rollback here
        }
    });
}
    EventWaitHandle _wait;
    private async void buttonStart_Click(object sender, EventArgs e) {
        _wait = new EventWaitHandle(true, EventResetMode.ManualReset);
        await Task.Run(() => GoInc());
    }
    private void buttonPause_Click(object sender, EventArgs e) {
        _wait.Reset();
    }
    private void buttonResume_Click(object sender, EventArgs e) {
        _wait.Set();
    }
    EventWaitHandle _wait;
    private void GoInc() {
        for (int i = 0; i < 10000; i++) {
            _wait.WaitOne();
            Thread.Sleep(100);
        }
    }