是否可以轮询任务完成?
我有一个长时间运行的任务,我不想阻止用户界面,所以我得到了一个DispatcherTimer
,我使用它的tick事件来检查任务属性IsCompleted
但这会导致某种形式的死锁,因为我的应用程序停止响应是否可以轮询任务完成?
public partial class MainWindow : Window
{
DateTime beginFirstPhase;
DateTime beginSecondPhase;
DispatcherTimer dispatcherTimer = new DispatcherTimer();
IEnumerable<string> collection;
Task firstPhaseTask;
Task secondPhaseTask;
public MainWindow()
{
InitializeComponent();
}
private void button_Click(object sender, RoutedEventArgs e)
{
progressTxtBox.AppendText("Entering button click event handler\n");
beginFirstPhase = DateTime.Now;
dispatcherTimer.Tick += DispatcherTimer_Tick_FirstPhase;
dispatcherTimer.Interval = new TimeSpan(0, 0, 5);
dispatcherTimer.Start();
progressTxtBox.AppendText("Begining First Phase now\n");
firstPhaseTask = Task.Factory.StartNew(() =>
/*this is basically a big linq query over a huge collection of strings
(58 thousand+ strings). the result of such query is stored in the field named
collection, above*/), TaskCreationOptions.PreferFairness);
progressTxtBox.AppendText("Awaiting First Phase completion...\n");
}
private void DispatcherTimer_Tick_FirstPhase(object sender, EventArgs e)
{
TimeSpan span = DateTime.Now - beginFirstPhase;
//not even the line bellow is executed.
statusTextBlock.Text = $"Running: {span.ToString()}";
if (firstPhaseTask.IsCompleted)
{
dispatcherTimer.Stop();
progressTxtBox.AppendText($"First Phase completed in {span.ToString()}\n");
secondPhase();
}
}
private void secondPhase()
{
beginSecondPhase = DateTime.Now;
progressTxtBox.AppendText("Begining Second Phase now\n"));
dispatcherTimer.Tick -= DispatcherTimer_Tick_FirstPhase;
dispatcherTimer.Tick += DispatcherTimer_Tick_SecondPhase;
dispatcherTimer.Interval = new TimeSpan(0, 0, 5);
dispatcherTimer.Start();
int counter = 0;
secondPhaseTask = Task.Factory.StartNew(() =>
{
foreach (string str in collection)
{
Dispatcher.Invoke(() => progressTxtBox.AppendText($"iteration <{counter++}>\n"));
IEnumerable<Tuple<string, string> queryResult; // = another linq query
foreach (var tuple in queryResult)
{
Dispatcher.Invoke(() => outputListView.Items.Add($"{tuple.Item1} {tuple.Item2} {str}"));
}
}
}, TaskCreationOptions.PreferFairness);
}
private void DispatcherTimer_Tick_SecondPhase(object sender, EventArgs e)
{
TimeSpan span = DateTime.Now - beginSecondPhase;
statusTextBlock.Text = $"Running: {span.ToString()}";
if (secondPhaseTask.IsCompleted)
{
dispatcherTimer.Stop();
progressTxtBox.AppendText($"Second Phase completed in {span.ToString()}\n");
progressTxtBox.AppendText("Execution Complete");
}
}
}
这是什么原因阻塞? Task.IsCompleted
是否阻止了调用者的线程? 难道根本就无法轮询这样的任务吗?如果不是,还有其他选择吗?
编辑:亲爱的估计StackOverflow社区的成员,我收到几个答案,基本上说“不要这样做,这样更好”。这些都是非常好的答案,我为他们感谢你们。但我的问题是这样的:“是否可以轮询任务完成,以及如何完成”。没关系,这是一个糟糕的设计模式,因为这不是问题的一部分。我知道我可以重写代码来使用await和异步,但是,由于我是自学的,因此我认为探索语言可以做什么是一个好主意。
这就像是我问了一个关于goto
如何工作的问题,我得到的所有答案都是为什么不使用goto
以及如何替换它。我明白你们都希望尽可能地提供帮助,为我提供我所选择的设计是不好的知识,但是如果不帮助我实施我的不良选择,那么你就拒绝了我有关如何去做的知识。我虚心相信这违背了这个社区的精神。我希望这个附录不会冒犯任何人,我只有尊重社区所有成员,我希望我已经说得很清楚。
你想通过使用await操作符来关闭Task.Run。这样你就可以告诉用户“正在等待......”,那么当任务完成时,你将自动在Gui线程上。如果您需要进度报告,我认为这是通过Progress类完成的,但不记得。反正这应该让你关闭...
private async void button_Click(object sender, RoutedEventArgs e)
{
progressTxtBox.AppendText("Entering button click event handler\n");
beginFirstPhase = DateTime.Now;
dispatcherTimer.Tick += DispatcherTimer_Tick_FirstPhase;
dispatcherTimer.Interval = new TimeSpan(0, 0, 5);
dispatcherTimer.Start();
progressTxtBox.AppendText("Begining First Phase now\n");
progressTxtBox.AppendText("Awaiting First Phase completion...\n");
firstPhaseTask =await Task.Factory.StartNew(() =>
/*this is basically a big linq query over a huge collection of strings
(58 thousand+ strings). the result of such query is stored in the field named
collection, above*/), TaskCreationOptions.PreferFairness);
progressTxtBox.AppendText("First Phase complete...\n");
}
我也建议改变的结果是...
有没有你不使用async
和await
理由吗?
await Task.Factory.StartNew(() =>
/*this is basically a big linq query over a huge collection of strings
(58 thousand+ strings). the result of such query is stored in the field named
collection, above*/), TaskCreationOptions.PreferFairness);
// StartSecondPhase won't get called until the Task returned by Task.Factory.StartNew is complete
await StartSecondPhase();
所以这意味着我不能定期轮询一个“任务”完成?因为第一阶段未完成,第二阶段无法开始 – FinnTheHuman
您可以进行投票,但为什么?没有理由,除非你想要目前的进展。 –
@JohnPeters我也想要当前的进展。如果我可以投票,我该怎么做?检查Task.IsCompleted显然会导致死锁。 – FinnTheHuman
就个人而言,我会处理这个方式: 找到你喜欢的节目忙碌的动画服务,谷歌是你的朋友。
调用忙动画(告诉用户等待,你可能会找到一个让你更新当前状态为好)
运行操作就像已经建议,但以最小的修改是这样的:
this.BusyService.ShowBusy();
Task t = Task.Factory.StartNew(() =>
PhaseOne();
PhaseTwo();
), TaskCreationOptions.PreferFairness);
t.ContinueWith(()=>{ this.BusyService.HideBusy(); });
我正在谈论showbusy的服务,因为我将它与Prism和WPF联系起来,但也有一个简单的WinForm可以实现这个技巧,甚至在这里也有示例示例。
会发生什么:UI被阻塞但不冻结,用户将知道他必须等待,在任务回调中您将释放UI。
希望得到这个帮助。
编辑: 让我们的方法,这一点不同,这里是为繁忙的指标的WinForms演示我说的是: BusySample
有一个BusyForm这仅仅是一个无模式形式多行文本框,在这里你”要去写你的更新文本。 另外还有谁这样使用它的主要形式有:
private BackgroundWorker worker;
private BusyForm busyForm = new BusyForm();
private string progressText;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
progressText = "Entering button click event handler" + Environment.NewLine;
busyForm.SetText(progressText);
worker = new BackgroundWorker();
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
worker.RunWorkerAsync();
busyForm.ShowDialog();
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
busyForm.Hide();
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
progressText += "Begining First Phase now" + Environment.NewLine;
this.Invoke((MethodInvoker)delegate
{
busyForm.SetText(progressText);
});
PhaseOne();
progressText += "First Phase complete..." + Environment.NewLine + "Begining Second Phase now" + Environment.NewLine;
this.Invoke((MethodInvoker)delegate
{
busyForm.SetText(progressText);
});
PhaseTwo();
progressText += "Execution Complete";
this.Invoke((MethodInvoker)delegate
{
busyForm.SetText(progressText);
});
System.Threading.Thread.Sleep(2000); //Just adding a delay to let you see this is shown
}
private void PhaseOne()
{
System.Threading.Thread.Sleep(2000);
}
private void PhaseTwo()
{
System.Threading.Thread.Sleep(2000);
}
的BackgroundWorker正在采取的所有东西的关心和它在一个单独的线程运行,反正你需要使用更新的文字:
this.Invoke((MethodInvoker)delegate
{
busyForm.SetText(progressText);
});
因为你需要从UI线程更新UI。我用10分钟的时间写了样本,我没有做太多的测试,但这应该会给你一个想法。
我不知道BusyService是什么。你能给我一个链接吗? – FinnTheHuman
这个建议使用将会阻塞直到完成的延续。对?如下所示使用await关键字。 –
@FinnTheHuman我用一个完整的工作样本更新了我的答案,即我对忙碌服务的含义。我在旅途中写了一个,但你可以改进它,或者只是在网上找到另一个。不幸的是,我无法给你一个链接或我在工作中使用的链接。 – Christopher
这不会编译:'firstPhaseTask = await TaskFactor.StartNew(...)'。另外,我第二阶段只能在第一阶段完成后才开始,否则对象将处于不一致状态。这就是为什么我需要调查第一阶段是否完成。 – FinnTheHuman
不,如果您使用闭包,则无需进行轮询,但它不会编译,因为您需要将代码更改为与我的建议类似。如果您需要查询结果,请按照建议中所示返回结果。当编译器命中await状态时,该线程会自动挂起,直到设置var结果的恢复。然后你是在主线程上,当第二次等待被击中时,如果你有一个“闭包”,同样的事情发生...... –