2012-01-05 96 views
0

我有两个函数ChangeText()& ChangeColor(),第一个函数名为ChangeText谁将加载大量的数据到内存中,这将花费大量的时间,所以我运行它异步;另一个叫做ChangeColor,当数据加载好时,它会改变按钮的颜色,所以有一个命令来运行这两个函数:第一个是ChangeText,第二个是ChangeColor。这里是我的代码:为什么EventWaitHandle不起作用?

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

这里是类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); 
     } 
    } 
} 

我拟用的EventWaitHandle同步这两个功能,但结果是,这两个函数将仍处于运行混乱顺序:有时会首先使用ChangeText()函数,有时会首先使用ChangeColor()。我只是很困惑。

还有,我使用线程池来启动这两项功能,但为什么我得到了相同的线程ID象下面这样:!
装载终了(Thread.CurrentThreadName = 10)着色面漆(Thread.CurrentThreadName = 10)

我以为Thread.CurrentThreadName会因为我使用线程池而不同!为什么? thx为您的答案。

回答

0
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; 
    } 
} 

查看上面的代码片段 - 它应该解释一下。当然,你有相同的线程的名字,因为你发送的标签委托给UI线程 - 这就是像你这样开始

你不应该做任何长时间的操作存在的首要原因
+0

嗨grzegorz_p,其实这是一个示例,但在实际工作中,ChangeColor函数将挑选出大量数据并与旧的txt文件进行比较,然后将数据着色为与旧数据相同。所以在这里我只是单独运行该功能。 thx为您的回复:) – CharlieShi 2012-01-05 09:05:34

+0

我已经做了一点调查,似乎我们有罪:) – 2012-01-05 10:02:30

+0

我不知道你的意思。我认为我们应该使用ThreadPool.QueueUserWorkItem(o => ChangeText()); ThreadPool.QueueUserWorkItem(o => ChangeColor());代替。 – CharlieShi 2012-01-05 14:08:34

0

关于您的问题(我在代码中看到其他可能的问题)我会尝试设置事件处理程序的构造并删除Change_Text方法的waitMeHandle.Reset();

当您并行启动两个进程时,您不能确定Change_Text将首先执行。

+0

Thx for SoMos的回复,以及删除waitMeHandle.Reset();它仍然不能正常工作。即使通过两个并行运行的进程,为什么我仍然不能使用EventWaitHandle来控制?你有这个好的解决方案吗?像使用互斥锁或连接或其他任何其他?你会指出可能的问题吗? thx :) – CharlieShi 2012-01-05 08:59:35

0

至于关于名称问题正在执行的线程如下:

如果您致电Dispatcher.Invoke,则指定的委托将在Dispatcher所关联的线程上执行。在你的情况下可能是UI线程。

参见MSDN的注释部分:

在WPF中,只有创建DispatcherObject的访问对象的线程。例如,从主UI线程分离出的后台线程无法更新在UI线程上创建的Button的内容。为了让后台线程访问Button的Content属性,后台线程必须将工作委托给与UI线程关联的Dispatcher。这是通过使用Invoke或BeginInvoke完成的。

接下来,你是过分的东西。如果您打电话给ThreadPool.QueueUserWorkItem,您正在调度一个代理以在ThreadPool线程上执行。现在在你的代码中,如果在ThreadPool线程中执行的方法中,你调用Func<T>.BeginInvoke,那么你再次安排一个委托在ThreadPool线程上执行。因此,只要改变你的代码如下:

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

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

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

是足够的线程池上的线程中执行ChangeTextChangeColor

+0

哦...... thx afrischke,我知道为什么threadname是一样的。我做了一个测试,只写了MessageBox.Show(Thread.CurrentThread.ManagedThreadId.ToString());在ChangeText函数和ChangeColor函数的前面,它向我展示了ThreadId是7和12.明白了,thx。 – CharlieShi 2012-01-05 09:25:23

+0

thx为您的帮助,现在所有的问题都解决了。非常感谢你的工作。 :) – CharlieShi 2012-01-05 10:04:04