0

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

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),它可以将请求直接委托给我的事务。而阻塞只会主动运行一个while循环来检查一些布尔属性。

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

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

4

3 回答 3

1

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

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
        }
    });
}
于 2013-10-09T14:15:55.763 回答
1

而阻塞只会主动运行一个while循环来检查一些布尔属性。

这不是阻塞,它被称为忙等待,这是你应该避免的事情。

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

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

在您的 UI 线程上:

shouldRollback = …;
shouldRollbackEvent.Set();

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

于 2013-10-09T15:11:32.583 回答
0
    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);
        }
    }
于 2016-02-16T22:38:55.343 回答