我有一个长期运行的任务,其行为类似于交易 - 它涉及许多操作,其中一个操作的成功取决于另一个操作的成功。
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循环检查一些bool属性。
现在我认为被动阻挡会更好,但我真的不知道该怎么做。你能告诉我吗?
编辑:我真的不想使用Thread.Sleep
,因为这些错误可能需要不同的时间来修复。取决于错误和正在修理错误的人。
答案 0 :(得分: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
}
});
}
答案 1 :(得分:1)
阻塞只会主动运行while循环检查一些bool属性。
没有阻止,它被称为忙碌等待,这是你应该避免的事情。
如果你想在两个线程之间进行这样的同步,一种方法是使用ManualResetEvent
:
AskUser(); // doesn't block
shouldRollbackEvent.WaitOne();
if (shouldRollback) …
在你的UI线程上:
shouldRollback = …;
shouldRollbackEvent.Set();
(这假设代码的两个部分都在同一个对象中执行。)
答案 2 :(得分: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);
}
}