所有线程完成后关闭加载表单

时间:2012-11-02 05:39:57

标签: c# winforms multithreading

我想知道我的所有异步线程何时完成,所以我知道何时关闭我的加载表单。我的代码永远不会关闭加载表单。我不知道为什么。我不确定如何正确地将我的ManualResetEvent对象传递给异步线程。

我也愿意采用一种更简单的方法来实现我知道何时关闭加载表单的目标。

更新

在阅读了这里的建议后,我更新了课程。不幸的是,它仍然无效。我感觉更接近了。只是回调永远不会发生。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading.Tasks;
using System.Threading;

namespace BrianTests
{

    public class TaskInfo
    {
        public RegisteredWaitHandle Handle;
        public string OtherInfo = "default";
        public Form loading;
    }


    public partial class AsyncControlCreateTest : Form
    {
        //List<ManualResetEvent> MREs = new List<ManualResetEvent>();
        Form loading = new Form() { Text = "Loading...", Width = 100, Height = 100 };
        CountdownWaitHandle cdwh;

        public AsyncControlCreateTest()
        {
            InitializeComponent();       
        }

        private void AsyncControlCreateTest_Load(object sender, EventArgs e)
        {            
            loading.Show(this);//I want to close when all the async threads have completed
            CreateControls();
        }  

        private void CreateControls()
        {
            int startPoint= 0;
            int threadCount = 2;
            cdwh = new CountdownWaitHandle(threadCount);

            for (int i = 0; i < threadCount; i++)
            {
                ManualResetEvent mre = new ManualResetEvent(initialState: true);                
                UserControl control = new UserControl() { Text = i.ToString() };                
                control.Load += new EventHandler(control_Load);
                Controls.Add(control);
                control.Top = startPoint;
                startPoint += control.Height;
                //MREs.Add(mre);
                //mre.Set();//just set here for testing
            }
            Task.Factory.StartNew(new Action(() => 
                {   
            TaskInfo info = new TaskInfo();
            info.loading = loading;
            try
            {
                info.Handle = ThreadPool.RegisterWaitForSingleObject(cdwh, WaitProc, info, 4000, executeOnlyOnce: false);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
                })); 
        }

        public static void WaitProc(object state, bool timedOut)
        {//this callback never occurs...
            TaskInfo ti = (TaskInfo)state;

            string cause = "TIMED OUT";
            if (!timedOut)
            {
                cause = "SIGNALED";
                // If the callback method executes because the WaitHandle is 
                // signaled, stop future execution of the callback method 
                // by unregistering the WaitHandle. 
                if (ti.Handle != null)
                    ti.Handle.Unregister(null);
            }

            Console.WriteLine("WaitProc( {0} ) executes on thread {1}; cause = {2}.",
                ti.OtherInfo,
                Thread.CurrentThread.GetHashCode().ToString(),
                cause
            );
            ti.loading.Close();
        }


        void control_Load(object sender, EventArgs e)
        {
            RichTextBox newRichTextBox = new RichTextBox();
            UserControl control = sender as UserControl;
            control.Controls.Add(newRichTextBox);            

            Task.Factory.StartNew(new Action(() => 
                {                    
                   Thread.Sleep(2000);
                   newRichTextBox.Invoke(new Action(() => newRichTextBox.Text = "loaded"));
                   cdwh.Signal();
                })); 
        }      
    }

    public class CountdownWaitHandle : WaitHandle
    {
        private int m_Count = 0;
        private ManualResetEvent m_Event = new ManualResetEvent(false);

        public CountdownWaitHandle(int initialCount)
        {
            m_Count = initialCount;
        }

        public void AddCount()
        {
            Interlocked.Increment(ref m_Count);
        }

        public void Signal()
        {
            if (Interlocked.Decrement(ref m_Count) == 0)
            {
                m_Event.Set();
            }
        }

        public override bool WaitOne()
        {
            return m_Event.WaitOne();
        }
    }
}

2 个答案:

答案 0 :(得分:1)

问题是WaitHandle.WaitAll抛出异常,你可以看到:

 try
 {
    WaitHandle.WaitAll(MREs.ToArray());
 }
 catch (Exception e) {
    MessageBox.Show(e.Message);
    throw;
 }

错误消息是“不支持STA线程上多个句柄的WaitAll”。 如果您执行类似

的操作
foreach(var m in MREs)
   m.WaitOne();

它会起作用。

我不太清楚为什么异常不会使应用程序崩溃,正如我所希望的那样。有关此内容,请参阅How can I get WinForms to stop silently ignoring unhandled exceptions?

答案 1 :(得分:0)

锁定MRE并将WaitAll从STA线程移开可以解决问题。

public partial class AsyncControlCreateTest : Form
{
    object locker = new object();
    static List<ManualResetEvent> MREs = new List<ManualResetEvent>();
    Form loading = new Form() { Text = "Loading...", Width = 100, Height = 100 };

    public AsyncControlCreateTest()
    {
        InitializeComponent();       
    }

    private void AsyncControlCreateTest_Load(object sender, EventArgs e)
    {            
        loading.Show(this);//I want to close when all the async threads have completed
        CreateControls();
    }  

    private void CreateControls()
    {
        int startPoint= 0;            
        for (int i = 0; i < 100; i++)
        {
            ManualResetEvent mre = new ManualResetEvent(initialState: false);                
            UserControl control = new UserControl() { Text = i.ToString() };                
            control.Load += new EventHandler(control_Load);
            Controls.Add(control);
            control.Top = startPoint;
            startPoint += control.Height;
            MREs.Add(mre);
        }
        Task.Factory.StartNew(new Action(() =>
        {
            try
            {
                WaitHandle.WaitAll(MREs.ToArray());
            }
            catch (Exception ex)
            {
                MessageBox.Show("error " + ex.Message);
            }
            finally
            {
                MessageBox.Show("MRE count = " + MREs.Count);//0 count provides confidence things are working...
                loading.Invoke(new Action( () => loading.Close()));
            }

        }));
    }

    void control_Load(object sender, EventArgs e)
    {
        RichTextBox newRichTextBox = new RichTextBox();
        UserControl control = sender as UserControl;
        control.Controls.Add(newRichTextBox);            

        Task.Factory.StartNew(new Action(() => 
            {                    
               Thread.Sleep(500);
               newRichTextBox.Invoke(new Action(() => newRichTextBox.Text = "loaded"));

               lock (locker)
               {
                   var ev = MREs.First();
                   MREs.Remove(ev);
                   ev.Set();
               }                   
            })); 
    }      
}