KMC
KMC

Reputation: 20046

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

enter image description here

Upvotes: 1

Views: 210

Answers (2)

Phillip Ngan
Phillip Ngan

Reputation: 16086

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.

Upvotes: 1

Evk
Evk

Reputation: 101443

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.

Upvotes: 1

Related Questions