从UI调度任务的问题。继续任务
我的应用程序使用以下代码安排长时间运行的任务:
Task.Factory.StartNew((a) => WorkTask1(), TaskCreationOptions.LongRunning | TaskCreationOptions.AttachedToParent) .ContinueWith(antecedent => WorkCompletedTask1(antecedent.Result), TaskScheduler.FromCurrentSynchronizationContext());
计划WorkCompletedTask1并按预期在UI上显示结果。 根据WorkTask1的结果,WorkCompletedTask1可以使用以下语句安排其他任务:
Task.Factory.StartNew((a) => WorkTask2(), TaskCreationOptions.LongRunning | TaskCreationOptions.AttachedToParent) .ContinueWith(antecedent => WorkCompletedTask2(antecedent.Result), TaskScheduler.FromCurrentSynchronizationContext());
WorkTask2不会按预期在单独的线程上运行; 它在UI线程上运行,该线程被阻塞直到WorkTask2完成。 我以为TaskCreationOptions.LongRunning会保证一个单独的线程。
为什么这不起作用的任何建议? 我可以从UI和非UI任务安排添加任务,而不是从UI中的.continuewith任务安排。
破碎的示例项目代码
在窗体上带有button1
按钮的空Windows窗体项目中,此代码无法按预期工作(Windows 7 VS2010 Express Net 4.0)。 T2和T3在UI线程中运行,而不是工作线程。 将listBox1添加到button1表单并尝试以下操作:
private delegate void DelegateSendMsg(String msg); private DelegateSendMsg m_DelegateSendMsg; private TaskScheduler uiSched; private Process thisProcess; private string thisProcessName, thisProcessId, uiThreadName, nonuiStatus = "Non-UI", uiStatus = "UI"; private void Form1_Load(object sender, EventArgs e) { thisProcess = Process.GetCurrentProcess(); thisProcessName = thisProcess.ProcessName; thisProcessId = thisProcess.Id.ToString(); uiThreadName = CurrentThread; m_DelegateSendMsg = this.SendMsg; uiSched = TaskScheduler.FromCurrentSynchronizationContext(); SendMsg("UI thread name is " + CurrentThread); } //create the name of the current task public string CurrentThread { get { string threadId = null; if (String.IsNullOrEmpty(Thread.CurrentThread.Name)) threadId = thisProcess.Id.ToString() + "=" + thisProcessName; else threadId = thisProcessId + "=" + thisProcessName + "/" + Thread.CurrentThread.Name; threadId += ":" + Thread.CurrentThread.ManagedThreadId + " "; return threadId; } } //validate if the function is running in the expected UI state or not public bool MeetsUIExpectations(string functionName, string expectedStatus) { bool rc = true; string currentThreadName = CurrentThread; string text = "Function " + functionName + " running in thread " + currentThreadName; if ((currentThreadName == uiThreadName) & expectedStatus == uiStatus) text += ": UI status as expected"; else if ((currentThreadName != uiThreadName) & expectedStatus == nonuiStatus) text += ": non-UI status as expected"; else { text += ": UI status is NOT as expected!" + " Expected: " + expectedStatus + "; running in thread" + currentThreadName; rc = false; } SendMsg(text); return rc; } //display a single text message private void SendMsg(String msg) { if (this.InvokeRequired) try { this.Invoke(m_DelegateSendMsg, "UI context switch: " + msg); } catch (Exception) { } else { listBox1.Items.Add(msg); listBox1.TopIndex = listBox1.Items.Count - 1; } } private void button1_Click(object sender, EventArgs e) { Task.Factory.StartNew((a) => T1(), TaskScheduler.Default, TaskCreationOptions.LongRunning | TaskCreationOptions.AttachedToParent) .ContinueWith(antecedent => T1Completed(antecedent.Result), uiSched); } private bool T1() { //get the name of the currently running function and validate UI status var currentMethod = System.Reflection.MethodInfo.GetCurrentMethod(); MeetsUIExpectations(currentMethod.ToString(), nonuiStatus); int i = 0; while (i < Int32.MaxValue) i++; return true; } private void T1Completed(bool successful) { var currentMethod = System.Reflection.MethodInfo.GetCurrentMethod(); MeetsUIExpectations(currentMethod.ToString(), uiStatus); if (successful) { Task.Factory.StartNew((a) => T2(), TaskScheduler.Default, TaskCreationOptions.LongRunning | TaskCreationOptions.AttachedToParent) .ContinueWith(antecedent => T2Completed(antecedent.Result), uiSched); } } private bool T2() { var currentMethod = System.Reflection.MethodInfo.GetCurrentMethod(); MeetsUIExpectations(currentMethod.ToString(), nonuiStatus); int i = 0; while (i < Int32.MaxValue) i++; return true; } private void T2Completed(bool successful) { var currentMethod = System.Reflection.MethodInfo.GetCurrentMethod(); MeetsUIExpectations(currentMethod.ToString(), uiStatus); Task.Factory.StartNew((a) => T3(), TaskScheduler.Default, TaskCreationOptions.LongRunning | TaskCreationOptions.AttachedToParent) .ContinueWith(antecedent => T3Completed(antecedent.Result), uiSched); } private bool T3() { var currentMethod = System.Reflection.MethodInfo.GetCurrentMethod(); MeetsUIExpectations(currentMethod.ToString(), nonuiStatus); int i = 0; while (i < Int32.MaxValue) i++; return true; } private void T3Completed(bool successful) { //get the name of the currently running function and validate UI status var currentMethod = System.Reflection.MethodInfo.GetCurrentMethod(); MeetsUIExpectations(currentMethod.ToString(), uiStatus); SendMsg("All functions completed"); }
在.NET 4.0中,您必须显式传递TaskScheduler.Default
。 你选择了错误的重载(见下文)。
一些一般的东西
在UI线程的延续中, TaskScheduler
仍然是FromCurrentSynchronizationContext
方法返回的UI线程 。 因此,除非您明确传递TaskScheduler
否则您启动的所有新Tasks
也将安排在UI线程上:
这是一个代码示例:
Task.Factory.StartNew(foo => {}, TaskScheduler.Default)
随意使用您需要的任何TaskScheduler
,但您需要明确说明它。
获得正确的超载
StartNew
有很多重载。 在下面的代码中,您选择了错误的代码,这会导致TaskScheduler.Default
充当state
(作为传递给T3
的值)而不是实际的调度程序 :
var options = TaskCreationOptions.LongRunning | TaskCreationOptions.AttachedToParent; // overload with Func, CancellationToken, options and TaskScheduler Task.Factory.StartNew (() => T2(), new CancellationToken(), options, TaskScheduler.Default); // overload with Func ((a) => T3(), TaskScheduler.Default, // state, not scheduler options);
显然,这样你就不会得到你想要的调度,但是上面描述的默认行为 。
.NET 4.5的附加信息
在.NET 4.5中,有TaskContinuationOptions.HideScheduler
来更改此行为。 有关新选项的更多详细信息,请参阅Stephen Toub在.NET 4.5中的新TaskCreationOptions和TaskContinuationOptions,并让我引用其中的代码示例:
// code sample copied from blog post stated above Task.Factory.StartNew(() => { // #2 long-running work, so offloaded to non-UI thread }).ContinueWith(t => { // #3 back on the UI thread Task.Factory.StartNew(() => { // #4 compute-intensive work we want offloaded to non-UI thread (bug!) }); }, CancellationToken.None, TaskContinuationOptions.HideScheduler, // <-- new option stated in text TaskScheduler.FromCurrentSynchronizationContext());
工作示例项目代码
在窗体上带有button1
按钮的空Windows窗体项目中,此代码按预期工作(Windows 7,.NET 4.0):
上述就是C#学习教程:从UI调度任务的问题。继续任务分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注---计算机技术网(www.ctvol.com)!
private void button1_Click(object sender, EventArgs e) { var uiSched = TaskScheduler.FromCurrentSynchronizationContext(); button1.Enabled = false; // this HardWork-task is not blocking, as we have // TaskScheduler.Default as the default scheduler Task.Factory.StartNew(HardWork) .ContinueWith(t => { button1.Enabled = true; // this HardWork-task will block, as we are on the // UI thread scheduler Task.Factory.StartNew(HardWork) .ContinueWith(t2 => { button1.Enabled = false; // this one will not, as we pass TaskScheduler.Default // explicitly Task.Factory.StartNew(HardWork, new CancellationToken(), TaskCreationOptions.None, TaskScheduler.Default).ContinueWith(t3 => { button1.Enabled = true; }, uiSched); // come back to UI thread to alter button1 }, uiSched); // come back to UI thread to alter button1 }, uiSched); // come back on UI thread to alter button1 } public void HardWork() { int i = 0; while(i < Int32.MaxValue) i++; }
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/951798.html