ホームページ >バックエンド開発 >C#.Net チュートリアル >C# マルチスレッド プログラミング

C# マルチスレッド プログラミング

高洛峰
高洛峰オリジナル
2016-12-12 14:42:101260ブラウズ

1. スレッドを使用する理由

1. スレッドは、コードを他のコードから分離し、アプリケーションの信頼性を向上させるために使用できます。

2. スレッドを使用してコーディングを簡素化できます。

3. スレッドを使用して同時実行を実現できます。

2. 基本知識

1. プロセスとスレッド: オペレーティング システム実行プログラムの基本単位として、プロセスはアプリケーションのリソースを所有します。プロセスのリソースはスレッドによって共有されます。独自のリソースではありません。

2. フォアグラウンド スレッドとバックグラウンド スレッド: Thread クラスを通じて作成された新しいスレッドは、デフォルトでフォアグラウンド スレッドになります。すべてのフォアグラウンド スレッドが閉じられると、すべてのバックグラウンド スレッドも例外をスローせずに直接終了されます。

3. サスペンド (サスペンド) とウェイクアップ (レジューム): スレッドの実行順序とプログラムの実行は予測できないため、サスペンドとウェイクアップの使用はデッドロックになりやすいため、できるだけ使用しないようにする必要があります。実際のアプリケーションでは。

4. スレッドのブロック: スレッドが終了するまで呼び出し元のスレッドをブロックします。

5. スレッドを終了します: 中止: ThreadAbortException 例外をスローして、スレッドを終了します。割り込み: ThreadInterruptException 例外をスローしてスレッドを終了し、例外をキャッチすることで実行を続行できます。

6. スレッドの優先順位: AboveNormal BelowNormal 最高最低 通常、デフォルトは通常です。

3. スレッドの使用

スレッド関数は、パラメーターなしで渡すことも、パラメーターを使用して渡すこともできます (パラメーターは 1 つだけ)。

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread t1 = new Thread(new ThreadStart(TestMethod));
            Thread t2 = new Thread(new ParameterizedThreadStart(TestMethod));
            t1.IsBackground = true;
            t2.IsBackground = true;
            t1.Start();
            t2.Start("hello");
            Console.ReadKey();
        }

        public static void TestMethod()
        {
            Console.WriteLine("不带参数的线程函数");
        }

        public static void TestMethod(object data)
        {
            string datastr = data as string;
            Console.WriteLine("带参数的线程函数,参数为:{0}", datastr);
        }
    } 
}

4. スレッド プール

スレッドの作成と破棄には一定量のオーバーヘッドが必要となるため、スレッドの過剰な使用はメモリ リソースの無駄を引き起こすため、パフォーマンスを考慮してスレッド プールの概念が導入されました。スレッド プールはリクエスト キューを維持し、スレッド プール コードはキューからタスクを抽出し、実行のためにスレッド プール内のスレッドに委任します。そのため、スレッドは実行直後に破棄されません。バックグラウンドやスレッドの作成と破棄にかかるコストを削減できます。

スレッドプールスレッドのデフォルトはバックグラウンドスレッド (IsBackground) です。

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            //将工作项加入到线程池队列中,这里可以传递一个线程参数
            ThreadPool.QueueUserWorkItem(TestMethod, "Hello");
            Console.ReadKey();
        }

        public static void TestMethod(object data)
        {
            string datastr = data as string;
            Console.WriteLine(datastr);
        }
    }
}

5. タスク クラス

ThreadPool の QueueUserWorkItem() メソッドを使用して非同期スレッドの実行を開始するのは非常に簡単ですが、このメソッドの最大の問題は、非同期スレッドの実行を開始するタイミングを通知する組み込みメカニズムがないことです。操作は完了しました。組み込みメカニズムはありますか? 組み込みメカニズムは、操作の完了後に戻り値を取得します。この目的のために、System.Threading.Tasks の Task クラスを使用できます。

Taskb54c2c292509147c0b54128f4eb90887 オブジェクトを構築し、汎用 TResult パラメーターの操作の戻り値の型を渡します。

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            Task<Int32> t = new Task<Int32>(n => Sum((Int32)n), 1000);
            t.Start();
            t.Wait();
            Console.WriteLine(t.Result);
            Console.ReadKey();
        }

        private static Int32 Sum(Int32 n)
        {
            Int32 sum = 0;
            for (; n > 0; --n)
                checked{ sum += n;} //结果太大,抛出异常
            return sum;
        }
    }
}

タスクが完了すると、新しいタスクが自動的に開始されます。
1 つのタスクが完了すると、スレッドをブロックせずに別のタスクを開始できます。

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            Task<Int32> t = new Task<Int32>(n => Sum((Int32)n), 1000);
            t.Start();
            //t.Wait();
            Task cwt = t.ContinueWith(task => Console.WriteLine("The result is {0}",t.Result));
            Console.ReadKey();
        }

        private static Int32 Sum(Int32 n)
        {
            Int32 sum = 0;
            for (; n > 0; --n)
                checked{ sum += n;} //结果溢出,抛出异常
            return sum;
        }
    }
}

6. デリゲートの非同期実行

デリゲートの非同期呼び出し: BeginInvoke() と EndInvoke()

namespace Test
{
    public delegate string MyDelegate(object data);
    class Program
    {
        static void Main(string[] args)
        {
            MyDelegate mydelegate = new MyDelegate(TestMethod);
            IAsyncResult result = mydelegate.BeginInvoke("Thread Param", TestCallback, "Callback Param");

            //异步执行完成
            string resultstr = mydelegate.EndInvoke(result);
        }

        //线程函数
        public static string TestMethod(object data)
        {
            string datastr = data as string;
            return datastr;
        }

        //异步回调函数
        public static void TestCallback(IAsyncResult data)
        {
            Console.WriteLine(data.AsyncState);
        }
    }
}

7. スレッド同期

1) アトミック操作 (インターロック): すべてのメソッドがアトミックな読み取りまたは書き込み操作を実行します。

2) lock() ステートメント: パブリック型をロックしないでください。ロックしないと、インスタンスがコード制御の範囲を超えてしまいます。ロックするプライベート オブジェクトを定義します。

3) Monitor はスレッド同期を実装します

Monitor.Enter() と Monitor.Exit() を通じて、排他的ロックが取得および解放され、取得後はリソースは排他的となり、他のスレッドはアクセスできません。

TryEnterメソッドもあり、リソースがリクエストできない場合はブロックせずに待機し、タイムアウトを設定して取得できない場合はfalseを返すことができます。

4) ReaderWriterLock

リソース操作に読み取りが多く書き込みが少ない場合、リソースの使用率を向上させるために、読み取り操作のロックは共有ロックになり、複数のスレッドがリソースを同時に読み取ることができますが、書き込み操作は排他的ロックになります。 1 つのスレッドのみの動作を許可します。

5)イベントクラスは同期を実装します

イベントクラスには終了状態と非終了状態の2つの状態があり、終了状態でWaitOneを呼び出すと成功を要求でき、Setを通じて時間ステータスを終了状態に設定できます。

1) AutoResetEvent(自動リセットイベント)

2) ManualResetEvent(手動リセットイベント)

6) Semaphore(セマフォ)

セマフォはカーネルオブジェクトが保持するint変数で、0の場合はスレッドがブロックされます。 、0 より大きい場合はブロック解除されます。セマフォ上の待機スレッドのブロックが解除されると、セマフォのカウントは +1 されます。

スレッドはWaitOneを通じてセマフォを1つ減らし、Releaseを通じてセマフォを1つ増やします。使い方はとても簡単です。

7) Mutex(ミューテックス)

排他的なリソースで、使い方はセマフォと似ています。

8) クロスプロセス同期

システムレベルの同期は、同期オブジェクトの名前を設定することで実現できます。異なるアプリケーションは、同期オブジェクトの名前を通じて異なる同期オブジェクトを識別します。


声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。