一个人完成后开始一个新的线程:为什么我不在一个新线程?

时间:2013-04-18 10:46:06

标签: c# multithreading winforms

这基本上就是代码:

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中使用控件进行编写而不使用InvokeRequiredMethodInvoker

我错过了一些观点?

2 个答案:

答案 0 :(得分:3)

尝试更改Task.Factory.StartNew(() => CopiaCartelle())  以下内容:

Task.Factory.StartNew(() => CopiaCartelle(),  CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default))

你在UI线程的延续中进入GeneraListaCartelleCompletata,并且它正在调度UI线程上的任务 - 将TaskScheduler.Default放入其自己的线程中运行。 (刚刚对此进行了测试以确认)

答案 1 :(得分:0)

@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标签停止跟随鼠标时,您会注意到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中执行的操作,例如更改标签文本。

如果您有任何疑问,请随时发表评论。