1

这不会给我带来任何错误,但是在执行第一个线程后它没有执行第二个线程。我做错什么了吗?

下面是我的代码: 我的按钮单击功能:

 private void ImportBothButtonclick(object sender, EventArgs e)
    {
        // Get the currently selected manufacturer from the combo box
        var selected = comboBox.SelectedItem;

        // Do we have one?
        if (selected != null)
        {
            // Extract the combo record
            var val= (ComboBoxItem)selected;

            // Do we have one?
            if (val != null)
            {
                // yes
                // Make this on a seperate thread so that the UI continues to work
                Invoke(new System.Action(() =>
                {
                    button1.Enabled = false;
                    button2.Enabled = false;
                    button3.Enabled = false;
                    var thread = new Thread(DoFunction1);

                    thread.Start(val); 
                }));

                Invoke(new System.Action(() =>
                {
                    button1.Enabled = false;
                    button2.Enabled = false;
                    button3Enabled = false;
                    var thread = new Thread(DoFunction2);

                    thread.Start(val);
                }));

            }
        }

    }
4

3 回答 3

4

这些动作不会做任何事情。这些操作是在您当前所在的同一线程上调用的。

线程当前正在并行运行。如果您希望这些线程串行运行而不是在 gui 线程上运行,您可以执行以下操作:

这是非任务版本。

// not allowed on a non-gui thread.
button1.Enabled = false;
button2.Enabled = false;
button3.Enabled = false;

new Thread(() =>
{
    DoFunction1();
    DoFunction2();

    // execute this on the gui thread. (winforms)
    this.Invoke(new Action( delegate
    {
        button1.Enabled = true;
        button2.Enabled = true;
        button3.Enabled = true;
    }));

}).Start();

如果您想并行运行它们,但要等到它们完成:

// not allowed on a non-gui thread.
button1.Enabled = false;
button2.Enabled = false;
button3.Enabled = false;

new Thread(() =>
{
    ManualResetEvent wait1 = new ManualResetEvent(false);
    ManualResetEvent wait2 = new ManualResetEvent(false);

    ThreadPool.QueueUserWorkItem((state) =>
        {
            DoFunction1();
            wait1.Set();
        });

    ThreadPool.QueueUserWorkItem((state) =>
        {
            DoFunction2();
            wait2.Set();
        });

    ManualResetEvent.WaitAll(new WaitHandle[] { wait1, wait2 });

    this.Invoke(new Action( delegate
        {
            // execute this on the gui thread. (winforms)
            button1.Enabled = true;
            button2.Enabled = true;
            button3.Enabled = true;
        }));
}).Start();

但这可以更容易使用任务。任务并行(任务并行库) http://msdn.microsoft.com/en-us/library/dd537609.aspx

于 2013-09-09T10:57:30.303 回答
2

请澄清您观察到的确切问题是什么?

根据您目前所说,问题在于“第二个线程没有在第一个线程之后运行”。

所以,让我来回答这个问题。

你的代码几乎没问题。您忽略了一件重要的事情:您的代码“new thread / thread.start()”实际上确实启动了一个新线程,然后它不会等待该线程执行或完成。

行:

new thread(f1).Start()
new thread(f2).Start()

不会“在线程 1 上运行 F1,然后在线程 2 上运行 F2”。相反,它们将“开始在线程 1 上运行 F1,并立即开始在线程 2 上运行 F2”。

要在 F1 完全完成后才执行 F2,您必须以某种方式将两者“链接”在一起:

  • 您可以创建简单的“议程”方法并运行它:

    private void doAllTasks()
    { 
        f1();
        f2();
    }
    
    new thread(doAllTasks).Start()
    
  • 您可以尝试通过 lambdas 动态“链接”它们,这实际上与上面的相同:

    new thread(() => { f1(); f2(); ).Start()
    
  • 您实际上可以立即运行它们,但是让 F2加入 [等待]直到 F1 的线程结束

    var th1 = new thread(f1);
    var th2 = new thread(() => {th1.Join(); f2();} ) 
    
    th1.Start();
    th2.Start();
    // note that this code is NOT perfect, there's some error handling to do here, etc..
    

或者,您可以为所有这些尝试一些漂亮而漂亮的包装器,例如 TPL 框架,如Sheridan 的回答中所见。

当然,您必须小心从其他新线程正在运行的内部触摸 UI 元素。Sheridan 的回答已经通过 TPL 方式涵盖了它。手动,您必须使用 Invoke/BeginInvoke 将与 UI 相关的代码反弹回 UI 线程。在您当前的代码中,您已经拥有它,但在那个地方,它不是必需的,因为 _Click 处理程序方法显然已经在 UI 线程中运行。

因此,您当前的示例可以简化为:

private void ImportBothButtonclick(object sender, EventArgs e)
{
    var selected = comboBox.SelectedItem;

    if (selected != null)
    {
        var val= (ComboBoxItem)selected;

        if (val != null)
        {
            // no Invoke needed, "button_click" handlers
            // are already on UI thread, so touching UI things is OK
            button1.Enabled = false;
            button2.Enabled = false;
            button3.Enabled = false;

            // starting a new thread also does not need Invoke
            var thread = new Thread(DoAllFunctions);
            thread.Start(val); 
        }
    }
}

private void DoAllFunctions(object something)
{
    DoFunction1(something);
    DoFunction2(something);

    // button1.Enabled = true; - cannot do it here because they are UI
    // button2.Enabled = true; - and DoAll is run from other thread.
    // button3.Enabled = true; - you must bounce that back to UI thread.

    LetTheUIKnowJobsAreFinished(); // <- performed here
}

private void LetTheUIKnowJobsAreFinished()
{
    Invoke(new Action(()=>{
        button1.Enabled = true;
        button2.Enabled = true;
        button3.Enabled = true;
    });
}

另外,作为最后一点,查看BackgroundWorker来自 System.ComponentModel。它有一组非常好的事件/回调,这将使所有线程交叉变得非常容易。

(顺便说一句。让我再说一遍:这段代码不可用。它只是一个草图。你会发现拼写错误、缺少冒号、缺少 try-catch-whatever 等等!)

于 2013-09-09T11:05:21.727 回答
1

也许你可以做这样的事情?:

Task.Factory.StartNew(new Action(() =>
{
    button1.Enabled = false;
    button2.Enabled = false;
    button3.Enabled = false;
}), new CancellationToken(), TaskCreationOptions.None, 
TaskScheduler.FromCurrentSynchronizationContext()).
ContinueWith(new Action(() => DoFunction1)).
ContinueWith(new Action(() =>
{
    button1.Enabled = false;
    button2.Enabled = false;
    button3Enabled = false;
}), TaskScheduler.FromCurrentSynchronizationContext()).
ContinueWith(new Action(() => DoFunction2));
于 2013-09-09T10:43:17.877 回答