gpt4 book ai didi

c# - 从 UI .continuewith 任务安排任务的问题

转载 作者:太空狗 更新时间:2023-10-30 00:33:03 24 4
gpt4 key购买 nike

我的应用程序使用以下代码安排了一个长时间运行的任务:

Task.Factory.StartNew<bool>((a) => WorkTask1(),
TaskCreationOptions.LongRunning | TaskCreationOptions.AttachedToParent)
.ContinueWith(antecedent => WorkCompletedTask1(antecedent.Result),
TaskScheduler.FromCurrentSynchronizationContext());

WorkCompletedTask1 已安排并按预期在 UI 上显示结果。根据 WorkTask1 的结果,WorkCompletedTask1 可以使用以下语句安排其他任务:

Task.Factory.StartNew<bool>((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<bool>((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<bool>((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<bool>((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 线程方法。因此,所有新的 Tasks除非你传递一个TaskScheduler,否则你的开始也被安排在UI线程上明确地:

这是一个代码示例:

Task.Factory.StartNew(foo => {}, TaskScheduler.Default)

随意使用任何TaskScheduler你需要,但你需要明确说明。

获得正确的重载

StartNew<T> 有很多重载.在下面的代码中,您选择了错误的代码,这会导致 TaskScheduler.Default充当state (作为 a 传递给 T3 的值)而不是实际调度程序:

var options = TaskCreationOptions.LongRunning
| TaskCreationOptions.AttachedToParent;

// overload with Func<bool>, CancellationToken, options and TaskScheduler
Task.Factory.StartNew<bool>(() => T2(), new CancellationToken(),
options, TaskScheduler.Default);

// overload with Func<object, bool> with state and options
// scheduler acts as state here instead of actual scheduler, and
// is therefore just passed as (a) to T3 (state is an object, thus
// can also be a TaskScheduler instance)
Task.Factory.StartNew<bool>((a) => T3(),
TaskScheduler.Default, // state, not scheduler
options);

显然,通过这种方式您不会获得所需的日程安排,而是上述默认行为

.NET 4.5 的附加信息

在 .NET 4.5 中,有 TaskContinuationOptions.HideScheduler 改变这种行为。参见 New TaskCreationOptions and TaskContinuationOptions in .NET 4.5 by Stephen Toub有关新选项的更多详细信息,让我引用其中的代码示例:

// 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):

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++;
}

关于c# - 从 UI .continuewith 任务安排任务的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13934274/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com