2012-01-05 101 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