简体   繁体   English

为什么EventWaitHandle不起作用?

[英]why does EventWaitHandle not work?

I have two functions named ChangeText() & ChangeColor(), the first function called ChangeText who will loading a large number of data into memory, it will cost a lot of time, So I run it asynchorously; 我有两个名为ChangeText()和ChangeColor()的函数,第一个名为ChangeText的函数会将大量数据加载到内存中,这将花费大量时间,因此我异步运行它; the other one is called ChangeColor who will change the button's color when data loading ok, so there is an order to run these two functions: ChangeText first and ChangeColor second. 另一个名为ChangeColor,它将在数据加载正常时更改按钮的颜色,因此有一个命令可以运行这两个功能:首先运行ChangeText,然后运行ChangeColor。 here is my code: 这是我的代码:

using System;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Threading;
using System.IO;

namespace ThreadSynchorous
{
    public partial class Window1 : Window
    {
        public Window1()
        {
        InitializeComponent();
        asyncInvoke = new AsyncInvoke();
    }
    AsyncInvoke asyncInvoke;
    EventWaitHandle waitMeHandle = new EventWaitHandle(false,EventResetMode.ManualReset);

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object state)
        {
            asyncInvoke.BeginAsync(ChangeText);
        }), null);

        ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object state)
        {
            asyncInvoke.BeginAsync(ChangeColor);
        }), null);

        label1.Content += " \r\n-------------------------\r\n";
    }

    private bool ChangeText()
    {
        waitMeHandle.Reset();
        this.button1.Dispatcher.Invoke(new Func<bool>(delegate()
        {
            string filename = @"C:\EXO.txt";
            using (StreamReader sr = new StreamReader(filename, Encoding.Default))
            {
                string result;
                while ((result = sr.ReadLine()) != null)
                {
                    //here perform action
                }
            }

            label1.Dispatcher.Invoke(new Func<bool>(delegate
            {
                label1.Content += "Loading finish!(Thread.CurrentThreadName="+Thread.CurrentThread.ManagedThreadId.ToString()+") ";
                waitMeHandle.Set();
                return true;
            }));
            waitMeHandle.Set();
            return true;
        }));
        waitMeHandle.Set();
        return true;
    }

    private bool ChangeColor()
    {
        waitMeHandle.WaitOne();
        this.button1.Dispatcher.Invoke(new Func<bool>(delegate()
        {
            this.button1.Background = Brushes.Red;

            label1.Dispatcher.Invoke(new Func<bool>(delegate()
            {
                label1.Content += "Coloring finish!(Thread.CurrentThreadName="+Thread.CurrentThread.ManagedThreadId+") ";
                return true;
            }));

            return true;
        }));
        return true;
    }
}
}

here is the class of AsyncInvoke: 这是AsyncInvoke的类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ThreadSynchorous
{
    public  class AsyncInvoke
    {
        public void BeginAsync(Func<bool> MyFunction)
        {
            Func<bool> func = new Func<bool>(MyFunction);
            IAsyncResult iar = func.BeginInvoke(new AsyncCallback(EndAsync), func);
        }

        public void EndAsync(IAsyncResult iar)
        {
            Func<bool> func = (Func<bool>)iar.AsyncState;
            func.EndInvoke(iar);
        }
    }
}

I planed to use EventWaitHandle to sync these two functions, but the result is that these two function will still running in a mess order: sometimes ChangeText() function first, sometimes ChangeColor() first. 我计划使用EventWaitHandle同步这两个函数,但结果是这两个函数仍将以混乱的顺序运行:有时首先使用ChangeText()函数,有时首先使用ChangeColor()。 I just so confused. 我很困惑。

And also, I use ThreadPool to start these two function, but why I got the same threadID like below: 而且,我使用ThreadPool启动这两个函数,但是为什么我得到如下相同的threadID:
Loading finish!(Thread.CurrentThreadName=10) Coloring finish!(Thread.CurrentThreadName=10) 加载完成!(Thread.CurrentThreadName = 10)着色完成!(Thread.CurrentThreadName = 10)

I thought that the Thread.CurrentThreadName will be different because I use the threadpool!!! 我以为Thread.CurrentThreadName会有所不同,因为我使用了线程池! why? 为什么? thx for your answer. 谢谢你的回答。

Regarding your question (I see other possible issues on the code) I would try to set the Event Handler on construction and remove the waitMeHandle.Reset(); 关于您的问题(我在代码上看到其他可能的问题),我将尝试在构造过程中设置事件处理程序并删除waitMeHandle.Reset(); of the Change_Text method. Change_Text方法。

As you're launching the two processes in parallel you can not be sure that Change_Text will be executed first. 当您同时启动两个进程时,不能确定将首先执行Change_Text

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
        asyncInvoke = new AsyncInvoke();
    }
    AsyncInvoke asyncInvoke;
    EventWaitHandle waitMeHandle = new EventWaitHandle(false, EventResetMode.ManualReset);

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object state)
        {
            asyncInvoke.BeginAsync(ChangeText);
        }), null);

        ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object state)
        {
            asyncInvoke.BeginAsync(ChangeColor);
        }), null);

        label1.Content += " \r\n-------------------------\r\n";
    }

    private bool ChangeText()
    {
        Debug.WriteLine("ChangeText");         

        //do your time-consuming operation here, controls' delegated are for UI updates only

        this.button1.Dispatcher.Invoke((Action)(()=>
        {
            Thread.Sleep(2000);
            Debug.WriteLine("Button invoker");
            //update button here


            //what was bool return type for?
            label1.Dispatcher.Invoke((Action)(() =>
            {
                label1.Content += "Loading finish!(Thread.CurrentThreadName=" + Thread.CurrentThread.ManagedThreadId.ToString() + ") ";
                waitMeHandle.Set();
            }));

        }));


        //waitMeHandle.Set(); - here's your guilty - button delegate runs asynchrounously so you had absolutely no guarantee that it's done as your app reach this line
        return true;
    }

    private bool ChangeColor()
    {
        waitMeHandle.WaitOne();
        Debug.WriteLine("ChangeColor");
        this.button1.Dispatcher.Invoke((Action)(() =>
        {
            this.button1.Background = Brushes.Red;

            label1.Dispatcher.Invoke((Action)(() =>
            {
                label1.Content += "Coloring finish!(Thread.CurrentThreadName=" + Thread.CurrentThread.ManagedThreadId + ") ";
                waitMeHandle.Reset(); //you've consumed your event here so this is the place to reset it
            }));
        }));
        return true;
    }
}

See code snippet above -it should explain you a bit. 请参见上面的代码段-应该向您解释一下。 And of course, you've got same thread name because you dispatch label delegate to UI thread - that's the primary reason you shouldn't do any lengthy operations there like you did initially 当然,您拥有相同的线程名称,因为您将标签委托分派给了UI线程-这是您不应该像最初那样进行任何冗长操作的主要原因

As far as the question regarding the name of the executing thread goes: 就有关执行线程名称的问题而言:

If you call Dispatcher.Invoke then the specified delegate will be executed on the thread the Dispatcher is associated with. 如果调用Dispatcher.Invoke则将在与Dispatcher关联的线程上执行指定的委托。 In your case probably the UI thread. 在您的情况下,可能是UI线程。

See the remarks section on MSDN : 请参阅MSDN上的备注部分:

In WPF, only the thread that created a DispatcherObject may access that object. 在WPF中,只有创建DispatcherObject的线程才能访问该对象。 For example, a background thread that is spun off from the main UI thread cannot update the contents of a Button that was created on the UI thread. 例如,从主UI线程分离出来的后台线程无法更新在UI线程上创建的Button的内容。 In order for the background thread to access the Content property of the Button, the background thread must delegate the work to the Dispatcher associated with the UI thread. 为了使后台线程访问Button的Content属性,后台线程必须将工作委派给与UI线程关联的Dispatcher。 This is accomplished by using either Invoke or BeginInvoke. 这可以通过使用Invoke或BeginInvoke来完成。

Next, you are overdoing things. 接下来,您正在做过多事情。 If you call ThreadPool.QueueUserWorkItem you are scheduling a delegate to execute on a ThreadPool thread. 如果调用ThreadPool.QueueUserWorkItem ,则正在调度委托以在ThreadPool线程上执行。 Now in your code, if within the method that gets executed on the ThreadPool thread, you call Func<T>.BeginInvoke , then again you schedule a delegate to get executed on a ThreadPool thread. 现在在您的代码中,如果在ThreadPool线程上执行的方法内,您调用Func<T>.BeginInvoke ,然后再次安排委托以在ThreadPool线程上执行。 So just changing your code to this: 因此,只需将代码更改为此:

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        ThreadPool.QueueUserWorkItem(o => ChangeText());

        ThreadPool.QueueUserWorkItem(o => ChangeColor());

        label1.Content += " \r\n-------------------------\r\n";
    }

Is sufficient for executing ChangeText and ChangeColor on ThreadPool threads. 足以在ThreadPool线程上执行ChangeTextChangeColor

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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