在一个按钮单击功能中访问两个线程

时间:2013-09-09 10:32:25

标签: c# multithreading winforms

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

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

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

            }
        }

    }

3 个答案:

答案 0 :(得分:4)

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

线程当前正在并行运行。如果你想让那些线程在串口但不在gui线程上运行,你可以这样做:

这是NON-TASK版本。

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

答案 1 :(得分:2)

请说明您观察到的确切问题是什么?

根据您目前的说法,问题在于“第一个线程后第二个线程未运行”。

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

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

行:

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

不会“在thread1上运行F1,然后在thread2上运行F2”。相反,他们将“开始在thread1上运行F1并立即开始在thread2上运行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;
    });
}

另外,作为最后一点,去查看System.ComponentModel中的BackgroundWorker。它有非常好的事件/回调集,可以使所有的穿越过程非常容易。

(顺便说一句。让我再说一遍:这段代码不可用。它只是一个草图。你会发现错别字,遗漏冒号,缺少尝试捕捉 - 等等!)

答案 2 :(得分: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));