【问题标题】:Close loading form when all threads are complete所有线程完成后关闭加载表单
【发布时间】:2012-11-02 05:39:56
【问题描述】:

我想知道我的所有异步线程何时完成,以便知道何时关闭加载表单。我的代码从不关闭加载表单。我不知道为什么。我也不确定如何正确地将 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();
        }
    }
}

【问题讨论】:

  • 也许值得尝试只创建 1 个异步线程并单步执行它以检查一切是否按预期运行?
  • WaitHandle.WaitAll(MREs.ToArray()) 将强制阻塞。您需要一种跟踪自己的机制,以了解线程何时完成处理。 stackoverflow.com/questions/4239609/…

标签: c# winforms multithreading


【解决方案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?

【讨论】:

    【解决方案2】:

    锁定 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();
                   }                   
                })); 
        }      
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多