0

スレッドで 3 つの乱数を生成し、それをリストに保存します。これが私のコードです

    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] = number; で ArgumentOutOfRangeException をスローすることがあります。2. (1) を過ぎた場合、結果のファイルにはまだ -1 が含まれています。

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

誰が何が間違っているか知っていますか? 前もって感謝します。

4

2 に答える 2

1

Dispatcher.BeginInvoke は、ディスパッチャーが関連付けられているスレッドで各アクションを呼び出すため、実際には異なるスレッドでアクションを実行していません。ThreadHandler メソッドでできるだけ多くのことを行い、BeginInvoke アクション内でのみ UI の変更を行う方がよい場合があります。

また、button1_Click では、各スレッドを開始する前に currentRecNumber をインクリメントします。これにより、最初のいくつかのスレッドがリストの最初のいくつかの項目をスキップします。

また、さまざまなスレッドから共有変数 (currentRecNumber、threadNumber、および myList) にアクセスしているため、あらゆる種類のスレッドの問題が発生する可能性があるため、大きな問題があります。各スレッドがこれらの変数から正しい値を読み書きしていることを確認するには、何らかの同期を使用する必要があります。InterlockedIncrementと InterlockedDecrement を使用して、これらの問題の一部を軽減できますが、すべてではありません。

また、スレッドの作成にはコストがかかることも指摘します。スレッド プールのスレッドで実行する作業をスケジュールするか、BackgroundWorkerを使用するか、Task Parallel Library や PLINQ などの並列処理ライブラリの 1 つを使用する方がはるかに優れています。

Joe Albahari によるスレッドに関する無料の電子ブックを読むことをお勧めします。

于 2013-01-21T06:53:37.220 に答える
0

マット、電子ブックをありがとう。とても分かりやすいです。ちょっとした追加でコードを修正することができました。その問題の鍵は「ラムダ式とキャプチャされた変数」にあるため、いくつかのローカル変数を追加します。

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();
            }
        }
    }
}
于 2013-01-22T10:34:21.860 に答える