2013-01-21 58 views
0

我想在線程中生成3個隨機數,然後將其保存到列表中。這裏是我的代碼C#wpf多線程

using System; 
    using System.Collections.Generic; 
    using System.Linq; 
    using System.Text; 
    using System.Windows; 
    using System.Windows.Controls; 
    using System.Windows.Data; 
    using System.Windows.Documents; 
    using System.Windows.Input; 
    using System.Windows.Media; 
    using System.Windows.Media.Imaging; 
    using System.Windows.Navigation; 
    using System.Windows.Shapes; 
    using System.Threading; 
    using System.IO; 

    namespace Multithreading1 
    { 
     /// <summary> 
     /// Interaction logic for MainWindow.xaml 
     /// </summary> 
     public partial class MainWindow : Window 
     { 
      List<int> myList = new List<int>(); 
      int threadNumber = 0; 
      int currentRecNumber = -1; 

      public MainWindow() 
      { 
       InitializeComponent(); 
      } 

      void ThreadHandler(int recNumber,int number) 
      { 
       Action action = null; 
       action =() => 
         { 
          myList[recNumber] = number; 
          ++currentRecNumber; 
          --threadNumber; 
          if (currentRecNumber < myList.Count) 
          { 
           ++threadNumber; 
           Thread t = new Thread(() => GetRandomNumber(currentRecNumber)); 
           t.Start(); 
          } 
          else 
           if (threadNumber == 0) //finish 
           { 
            List<String> stringList = new List<String>(); 
            for (int i = 0; i < myList.Count;i++) 
            { 
             stringList.Add(myList[i].ToString()); 
            } 
            File.WriteAllLines("C:\\Users\\Public\\Documents\\MyList.txt", stringList); 
            System.Windows.MessageBox.Show("Finish"); 
           } 
         }; 
       this.Dispatcher.BeginInvoke(action); 
      } 

      void GetRandomNumber(int recNumber) 
      { 
       Random rnd = new Random(); 
       int randomInt = rnd.Next(1, 13); 
       ThreadHandler(recNumber, randomInt); 
      } 

      private void button1_Click(object sender, RoutedEventArgs e) 
      { 
       for (int i = 0; i < 20; i++) 
       { 
        myList.Add(-1); 
       } 
       for (int i = 0; i < 3; i++) 
       { 
        ++currentRecNumber; 
        ++threadNumber; 
        Thread t = new Thread(() => GetRandomNumber(currentRecNumber)); 
        t.Start(); 
       } 
      } 
     } 
    } 

的問題是:1。 有時在myList中[recNumber] =號拋出ArgumentOutOfRangeException; 2.如果闖過(1)生成的文件仍然包含-1s,如:

-1 
-1 
8 
6 
11 
-1 
1 
3 
-1 
3 
3 
8 
8 
8 
8 
10 
10 
10 
10 
12 

任何人都知道什麼是錯的? 在此先感謝。

回答

1

您的Dispatcher.BeginInvoke將調用與調度程序關聯的線程上的每個操作,因此您實際上並未實際在不同線程上運行該操作。在ThreadHandler方法中儘可能多地執行操作可能會更好,並且只能在BeginInvoke操作內部進行UI更改。

同樣在您的button1_Click中,您在啓動每個線程之前遞增currentRecNumber,這樣會導致前幾個線程跳過列表中的前幾個項目。

您還有一個主要問題,因爲您正在訪問來自不同線程的共享變量(currentRecNumber,threadNumber和myList),這會導致各種線程問題。您需要使用某種同步來確保每個線程正在從這些變量讀取和寫入正確的值。您可以使用InterlockedIncrement和InterlockedDecrement來減輕其中一些問題,但不是所有這些問題。

我還會指出,創建線程很昂貴,最好在thread pool threads上安排您想做的工作,使用BackgroundWorker或使用其中一個並行庫,如Task Parallel Library或PLINQ。

我建議在Joe Albahari的線程上閱讀this free ebook

+0

我以爲this.Dispatcher.BeginInvoke(action)會在主線程上串行執行動作。有沒有辦法做到這一點 ? – Irwan

+0

類似PostMessage – Irwan

+0

是的。該動作僅由您的主線程執行.. –

0

感謝馬特的電子書。這很容易理解。我設法修改了我的代碼,並增加了一些小部分其問題的關鍵是「Lambda表達式和捕獲變量」,所以我添加了一些局部變量。

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Data; 
using System.Windows.Documents; 
using System.Windows.Input; 
using System.Windows.Media; 
using System.Windows.Media.Imaging; 
using System.Windows.Navigation; 
using System.Windows.Shapes; 
using System.Threading; 
using System.IO; 

namespace Multithreading1 
{ 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window 
    { 
     List<int> myList = new List<int>(); 
     int threadNumber = 0; 
     int currentRecNumber = -1; 

     public MainWindow() 
     { 
      InitializeComponent(); 
     } 

     void ThreadHandler(int recNumber,int number) 
     { 
      Action action = null; 
      action =() => 
        { 
         myList[recNumber] = number; 
         ++currentRecNumber; 
         --threadNumber; 
         int localCurrentRecNumber = currentRecNumber; 
         int localThreadNumber = threadNumber; 
         if (localCurrentRecNumber < myList.Count) 
         { 
          ++threadNumber; 
          Thread t = new Thread(() => GetRandomNumber(localCurrentRecNumber)); 
          t.Start(); 
         } 
         else 
          if (localThreadNumber == 0) //finish 
          { 
           List<String> stringList = new List<String>(); 
           for (int i = 0; i < myList.Count;i++) 
           { 
            stringList.Add(myList[i].ToString()); 
           } 
           File.WriteAllLines("C:\\Users\\Public\\Documents\\MyList.txt", stringList); 
           System.Windows.MessageBox.Show("Finish"); 
          } 
        }; 
      this.Dispatcher.BeginInvoke(action); 
     } 

     void GetRandomNumber(int recNumber) 
     { 
      Random rnd = new Random(); 
      int randomInt = rnd.Next(1, 13); 
      ThreadHandler(recNumber, randomInt); 
     } 

     private void button1_Click(object sender, RoutedEventArgs e) 
     { 
      for (int i = 0; i < 20000; i++) 
      { 
       myList.Add(-1); 
      } 
      for (int i = 0; i < 3; i++) 
      { 
       ++currentRecNumber; 
       ++threadNumber; 
       int localCurrentNumber = currentRecNumber; 
       Thread t = new Thread(() => GetRandomNumber(localCurrentNumber)); 
       t.Start(); 
      } 
     } 
    } 
}