0

一般的なハンドラーメソッドで処理されるイベントをスローするワーカースレッドを取得しました。実際には、イベントをスローするワーカースレッドを含むオブジェクトを作成したスレッドによってハンドラーメソッドが実行されるようにしたいと思います。

私は次のコードを試しました:

public partial class Form1 : Form
{
    public event EventHandler Tested;

    public Form1()
    {
        InitializeComponent();

        Tested += new EventHandler(Form1_Tested);
    }

    private void Form1_Tested(object sender, EventArgs e)
    {
        Text = "Test";
    }

    private void Form1_Shown(object sender, EventArgs e)
    {
        #region Test 1
        // Works normal in GUI-Thread directly
        Form1_Tested(this, EventArgs.Empty);
        #endregion

        #region Test 2
        // Works in worker thread and throws GUI exception 
        // because of foreign thread access
        new Thread(new ThreadStart(delegate()
            {
                OnTested(EventArgs.Empty);
            })).Start();
        #endregion

        #region Test 3
        // Works in worker thread and returns to GUI-Thread
        // to throw the event, so no need in eventhandler 
        // to use Control.Invoke();
        AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(null);
        new Thread(new ThreadStart(delegate()
            {
                asyncOp.Post(new SendOrPostCallback(delegate(object obj)
                    {
                        OnTested(EventArgs.Empty);
                    }), null);
            })).Start();
        #endregion
    }

    protected virtual void OnTested(EventArgs e)
    {
        EventHandler tmpHandler = Tested;
        if (tmpHandler != null)
            tmpHandler(this, e);
    }
}

使用しているスレッドをクローザーで見てみました

Thread.CurrentThread.ManagedThreadId;

ワーカースレッドを開始する前は、メインスレッドのIDは「1」です。UIスレッド内で、イベントを発生させる直前のスレッドID(ワーカースレッドの)は「4」です。ここで、ハンドラーメソッドのスレッドIDを詳しく見てみると、スレッドIDは「10」のようなものですが、代わりにIDが「1」であると予想していました。私はそれについて非常に混乱しています。

一般的に:私のスニペットはそれを行う正しい方法ですか、それともこれを行うためのベストプラクティス(または実際の例)は何ですか?

実際にワーカースレッドでInvokeを使用することは避けたかったのです。

よろしく、トーマス

4

1 に答える 1

1

デリゲートの代わりにTask<>を使用できますか?できれば、それはあなたのために物事を単純化するかもしれないと思います。

基本的に、Task <>を作成する場合は、スレッドが終了するのを待ってからリターンコードを返すか、スレッドが例外をスローした場合は.Resultプロパティにアクセスすることで、そのタスクの結果にアクセスできます。 .Resultプロパティにアクセスしているスレッドのコンテキストでその例外を再スローします。

(スレッドが何も返さない場合は、.Resultにアクセスする代わりにTask.Wait()を呼び出すことができます)

いくつかのコードは、私が何を意味するかを説明するのに役立つかもしれません:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace Demo
{
    class Program
    {
        static void Main(string[] args)
        {
            var task = new Task<int>(() => test(1000, "Test1"));
            tryTask(task);

            task = new Task<int>(() => test(2000, "Test2"));
            tryTask(task);

            task = new Task<int>(() => test(1, "Test2"));
            tryTask(task);
        }

        static void tryTask(Task<int> task)
        {
            task.Start();

            try
            {
                Console.WriteLine("Task result = " + task.Result);
            }

            catch (AggregateException ex)
            {
                Console.WriteLine("Task threw an exception: " + ex.InnerException.Message);
            }
        }

        static int test(int value, string name)
        {
            Console.WriteLine("Starting thread " + name);
            Thread.Sleep(value);
            Console.WriteLine("Ending thread " + name);

            if (value == 1) // Magic number!
            {
                throw new InvalidOperationException("Test Exception");
            }

            return value;
        }
    }
}
于 2012-06-21T12:56:42.307 に答える