这基本上就是代码:
private void TaskGestioneCartelle()
{
Task.Factory.StartNew(() => GeneraListaCartelle())
.ContinueWith(t => GeneraListaCartelleCompletata()
, CancellationToken.None
, TaskContinuationOptions.None
, TaskScheduler.FromCurrentSynchronizationContext());
}
private void GeneraListaCartelle()
{
// ... code
}
private void GeneraListaCartelleCompletata()
{
Task.Factory.StartNew(() => CopiaCartelle())
.ContinueWith(t => CopiaCartelleCompletato()
, CancellationToken.None
, TaskContinuationOptions.None
, TaskScheduler.FromCurrentSynchronizationContext());
}
private void CopiaCartelle()
{
// long operation...
}
事实上,当CopiaCartelle启动时,我不进入新线程,因为它需要很多时间,UI完全冻结(而在GeneraListaCartelle()
上,这也需要很长时间,这不会发生)。也因为我可以在UI中编写控件而不使用InvokeRequired
和MethodInvoker
。
我漏了一些点吗?
尝试更改Task.Factory.StartNew(() => CopiaCartelle())
Task.Factory.StartNew(() => CopiaCartelle(), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default))
你要进入GeneraListaCartelleCompletata在UI线程上的延续,它正在调度UI线程上的任务-放置TaskScheduler。默认情况下,它会在自己的线程中运行。
@NDJ发布的内容是正确的,我已经做了一个快速的示例来展示正在发生的事情。
首先,方法:
private static void TaskGestioneCartelle()
{
Task.Factory.StartNew(() => GeneraListaCartelle())
.ContinueWith(t => GeneraListaCartelleCompletata()
, CancellationToken.None
, TaskContinuationOptions.None
, TaskScheduler.FromCurrentSynchronizationContext());
}
private static void GeneraListaCartelle()
{
//No sleep could block the thread UI because the task is being executed on a different Thread
Debug.WriteLine("GeneraListaCartelle " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(4000);
mainForm.Invoke(new Action(() => bla.Text = "uno due tre, Genera Lista!"));
}
private static void GeneraListaCartelleCompletata()
{
//This is begin executed on the UI thread
Debug.WriteLine("GeneraListaCartelleCompletata " + Thread.CurrentThread.ManagedThreadId);
Task.Factory.StartNew(() => CopiaCartelle())
.ContinueWith(t => CopiaCartelleCompletato()
, CancellationToken.None
, TaskContinuationOptions.None
, TaskScheduler.FromCurrentSynchronizationContext());
}
private static void CopiaCartelle()
{
//This is begin executed on the UI thread (doesn't even show in the form 'cause the thread is blocked)
Debug.WriteLine("CopiaCartelle " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(4000);
mainForm.Invoke(new Action(() => bla.Text = "Copia Cartelle \o"));
}
private static void CopiaCartelleCompletato()
{
//This is begin executed on the UI thread
Debug.WriteLine("CopiaCartelleCompletato " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(4000);
mainForm.Invoke(new Action(() => bla.Text = "Completato!"));
//Stops blocking the UI thread
}
现在是表单和组件
static Label bla = new Label()
{
Text = "Mama Mia, Gestione Cartelle!",
Left = 100,
Top = 100,
Width=300
};
static Label hangOn = new Label()
{
Text="Hang loose"
};
static Form mainForm = new Form()
{
Width = 600,
Height = 600
};
[STAThread]
static void Main(string[] args)
{
mainForm.Controls.Add(bla);
mainForm.Controls.Add(hangOn);
mainForm.MouseMove += (o, e) => { hangOn.Left = e.X; hangOn.Top = e.Y; };
Debug.WriteLine("UI Thread: "+ Thread.CurrentThread.ManagedThreadId);
TaskGestioneCartelle();
Application.Run(mainForm);
}
首先,运行应用程序并继续移动鼠标。您将注意到,当Hang Loose
Label停止跟随鼠标时,UI线程被阻塞。
现在,如果您从Debug中检查Output
,您将看到类似这样的内容:
UI Thread: 10
GeneraListaCartelle 6
GeneraListaCartelleCompletata 10
CopiaCartelle 10
CopiaCartelleCompletato 10
看到了?它使用UI线程来运行你的任务,因此挂起你的UI。
现在,把TaskScheduler.FormCurrentSynchronizationContext()
改成TaskScheduler.Default
的大作:
UI Thread: 8
GeneraListaCartelle 9
GeneraListaCartelleCompletata 10
CopiaCartelle 10
CopiaCartelleCompletato 11
值得注意的是,我使用mainForm.Invoke
来调用要在UI Thread
中执行的操作,例如更改标签文本。
如果你有任何疑问,请随意评论。