简体   繁体   中英

Scheduling with EventWaitHandle with Dispatcher.BeginInvoke

The following piece of code has two threads each writing 20's string str to its corresponding textbox. Once completed, Thread t00 signals Thread t01 to start and change the shared string str from y to x . Thread t00 should write 20 y to a textbox, and Thread t01 should write 20 x to another textbox. Instead, Thread t00 end up writing 19 y and 1 x . But if I add a Thread.Sleep() before EventWaitHandle is set, that fixes the problem I had (I get 20 x and 20 y ), but why? EventWaitHandle should not be set only after the loop is finished, with or without Thread.Sleep() .

public partial class MainWindow : Window
{
    public string str = "y";
    static EventWaitHandle _waitHandle = new AutoResetEvent(false);

    public MainWindow()
    {
        InitializeComponent();

        Thread t00 = new Thread(() =>
        {
            for (int i = 0; i < 20; i++)
            {
                Thread.Sleep(200);
                Action action00 = () =>
                {
                    tb00.AppendText(str);
                };
                Dispatcher.BeginInvoke(action00);
            }
            Thread.Sleep(200);  // <-- why this fix the problem??
            _waitHandle.Set();
        });
        t00.Start();


        Thread t01 = new Thread(() =>
        {
            Action action00 = () =>
            {
                tb01.AppendText("Waiting...\n");
            };
            Dispatcher.BeginInvoke(action00);
            _waitHandle.WaitOne();

            str = "x";
            for (int i = 0; i < 20; i++)
            {
                Thread.Sleep(200);
                Action action = () =>
                {
                    tb01.AppendText(str);
                };
                Dispatcher.BeginInvoke(action);
            }
        });
        t01.Start();
    }
}

在此处输入图片说明

Because you are using BeginInvoke . BeginInvoke invokes delegate on UI thread asynchronously. It puts a message to UI thread message queue and returns, so the fact it returned does not mean action was actually executed.

For that reason, in most cases, when you set a wait handle and another thread recieves singal and changes str from y to x - there is still non-invoked tb00.AppendText(str); delegate in UI thread queue. When it finally is invoked - str is already x .

Thread.Sleep "fixes" that, because it gives some time for that pending delegate to execute on UI thread. Using Invoke instead of BeginInvoke also "fixes" that, because Invoke is synchronous and returns only after delegate has actually been executed on UI thread.

Think of BeginInvoke() pushing a unit of work into a queue. When the 20th action00 is popped from the work queue and executed, the str='x' has already executed, and so when the action00 is actually run, it will print an x .

The Thread.Sleep(200) gives the 20th action00 time to be popped and run before the str='x' is execute, hence it will print a y .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM