1

いくつかの作業を行うためにスレッドを生成する主なタスクがあります。作業が完了すると、コンソールに書き込みます。

私の問題は、後で作成されたスレッドの一部が、以前に作成されたスレッドよりも速く終了することです。ただし、スレッドが作成されたのとまったく同じ順序でコンソールに書き込む必要があります。

そのため、スレッドがタスクを完了し、以前のスレッドが完了していない場合、それらの以前のスレッドも完了するまで待機する必要があります。

    public class DoRead
    {
        public DoRead()
        {
        }

        private void StartReading()
        {
            int i = 1;

            while (i < 10000)
            {
                Runner r = new Runner(i, "Work" + i.ToString());
                r.StartThread();
                i += 1;
            }
        }
    }

    internal class Runner : System.IDisposable
    {
        int _count;
        string _work = "";

        public Runner(int Count, string Work)
        {
            _count = Count;
            _work = Work;
        }

        public void StartThread()
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(runThreadInPool), this);
        }

        public static void runThreadInPool(object obj)
        {
            ((Runner)obj).run();
        }

        public void run()
        {
            try
            {
                Random r = new Random();
                int num = r.Next(1000, 2000);

                DateTime end = DateTime.Now.AddMilliseconds(num);
                while (end > DateTime.Now)
                {
                }

                Console.WriteLine(_count.ToString() + " : Done!");
            }
            catch
            {
            }
            finally
            {
                 _work = null;
            }
        }

        public void Dispose()
        {
             this._work = null;
        }

    }
4

3 に答える 3

1

これを行うには、私が使用したよりも簡単な方法があるかもしれません (私は .Net 4.0 に慣れています)。

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace ConsoleApplication5
{
    class Program
    {
        public static readonly int numOfTasks = 100;

        public static int numTasksLeft = numOfTasks;

        public static readonly object TaskDecrementLock = new object();

        static void Main(string[] args)
        {
            DoRead dr = new DoRead();

            dr.StartReading();

            int tmpNumTasks = numTasksLeft;

            while ( tmpNumTasks > 0 )
            {
                Thread.Sleep(1000);
                tmpNumTasks = numTasksLeft;
            }


            List<string> strings = new List<string>();

            lock( DoRead.locker )
            {
                for (int i = 1; i <= Program.numOfTasks; i++)
                {
                    strings.Add( DoRead.dicto[i] );
                }
            }

            foreach (string s in strings)
            {
                Console.WriteLine(s);
            }

            Console.ReadLine();
        }

        public class DoRead
        {

            public static readonly object locker = new object();

            public static Dictionary<int, string> dicto = new Dictionary<int, string>();

            public DoRead()
            {
            }

            public void StartReading()
            {
                int i = 1;

                while (i <= Program.numOfTasks )
                {
                    Runner r = new Runner(i, "Work" + i.ToString());
                    r.StartThread();
                    i += 1;
                }


            }
        }

        internal class Runner : System.IDisposable
        {
            int _count;
            string _work = "";

            public Runner(int Count, string Work)
            {
                _count = Count;
                _work = Work;
            }

            public void StartThread()
            {
                ThreadPool.QueueUserWorkItem(new WaitCallback(runThreadInPool), this);
            }

            public static void runThreadInPool(object obj)
            {
                Runner theRunner = ((Runner)obj);
                string theString = theRunner.run();

                lock (DoRead.locker)
                {
                    DoRead.dicto.Add( theRunner._count, theString);
                }

                lock (Program.TaskDecrementLock)
                {
                    Program.numTasksLeft--;
                }
            }

            public string run()
            {
                try
                {
                    Random r = new Random();
                    int num = r.Next(1000, 2000);

                    Thread.Sleep(num);

                    string theString = _count.ToString() + " : Done!";

                    return theString;

                }
                catch
                {
                }
                finally
                {
                    _work = null;
                }

                return "";
            }

            public void Dispose()
            {
                this._work = null;
            }

        }
    }
}

基本的に、各タスクから出力したい文字列を辞書に保存します。インデックスはタスク番号です。(辞書へのアクセスを安全にするためにロックを使用します)。

次に、すべてのバックグラウンド スレッドが完了するまでメイン プログラムが待機するように、NumTasksLeft 変数への別のロックされたアクセスを使用しました。

ランナーのコールバックに何かを追加しました。

ビジー ループを使用するのは悪い習慣なので、Thread.Sleep( num ) ステートメントに変更しました。

例に合わせて numOfTasks を 10000 に変更するだけです。

戻り文字列を辞書から順番に取り出して、画面に出力します。

これをリファクタリングしてグローバル変数を移動または処理できると確信していますが、これは機能します。

また、コマンドでロックを使用していないことに気付いたかもしれません

 tmpNumTasks = numTasksLeft;

numTasksLeft は int であり、32 ビット以上のコンピューターではアトミックに読み取られるため、これはスレッドセーフです。

于 2012-07-27T03:59:16.097 に答える
0

通常、このような要件は、すでに行っているように、増分シーケンス番号で満たされます。

通常、処理スレッドからの出力は、順不同のすべての結果オブジェクトのリスト (または辞書) を含むフィルター オブジェクトを介してフィードされ、より低いシーケンス番号を持つすべての結果が来るまで「それらを保持」します。繰り返しますが、すでに行ったことと同様です。

どんな種類の sleep() ループも必要ありません。作業スレッド自体がフィルター オブジェクトを操作することも (これはロックになります)、または作業スレッドは、アウトオブオーダー フィルターを操作する「出力スレッド」に結果をプロデューサー/コンシューマー キューに入れることもできます。

このスキームは、プールされた作業スレッドでうまく機能します。継続的な作成/終了/破棄のオーバーヘッドがないもの。

于 2012-07-27T09:41:41.497 に答える
0

私はC#についてあまり知りませんが、マルチスレッドの全体的な考え方は、複数のスレッドが独立して実行されており、どれが早く終了するかを決して知ることができないということです(そして、以前のスレッドが早く終了することを期待すべきではありません)。

1 つの回避策は、代わりに処理スレッドで終了メッセージを書き出すことです。処理スレッドにどこかにフラグを設定させ (おそらく、要素のないリスト = 生成されたスレッドの数がない)、別のスレッドに基づいて終了メッセージを出力させます。そのリスト内のフラグを表示し、前のフラグが連続して「終了」した位置まで報告します。

正直なところ、とにかくこのような終了メッセージを印刷するのは合理的ではないと思います. そういう無意味な「機能」はデザインを変えたほうがずっといいと思います。

于 2012-07-27T03:24:33.257 に答える