2013-01-21 59 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(); 
      } 
     } 
    } 
}