1

すべての非同期スレッドがいつ完了したかを知りたいので、読み込みフォームをいつ閉じるかを知りたいです。私のコードはロードフォームを閉じません。どうしてか分かりません。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();
        }
    }
}
4

2 に答える 2

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

それが動作します。

私が望んでいたように、例外がアプリケーションをクラッシュさせなかった理由はよくわかりません。おそらく、未処理の例外を黙って無視するのを WinForms で停止するにはどうすればよいですか? を参照してください。このため。

于 2012-11-02T08:57:30.073 に答える
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();
               }                   
            })); 
    }      
}
于 2012-11-02T14:31:25.357 に答える