C# マルチスレッド - 「Double Color Ball」プロジェクト実装の基本概念 (ナレッジ ポイント ディレクトリ、コード、ビデオ付き)

概要: このビデオでは、スレッドの基本概念から、マルチスレッドについて非常に徹底的に説明しています -> .net のさまざまなバージョンで使用されるスレッド化メソッド -> マルチスレッドに関する一般的な問題 -> Shuangseqiu プロジェクトの実践、すべての知識ポイントにはコードの実践が含まれています。あなたはそれから恩恵を受けるでしょう 非常に浅いです。学習メモと実践的なコードが添付されています。
ビデオ

目次

1. スレッドとプロセスの概念とその利点と欠点

スレッド: プログラム実行の最小単位。すべての操作はスレッドによって完了します。同期が使用される場合、リソースは常にこのスレッドによって占有されるため、他のインターフェイスは動作できず、インターフェイスはスタックします。
ここに画像の説明を挿入します
ここに画像の説明を挿入します
ここに画像の説明を挿入します

ここに画像の説明を挿入します
ここに画像の説明を挿入します
1. 始まりは制御できない 2. 終わりは制御できない
ここに画像の説明を挿入します
メインスレッドコード
時間 (i++) とリソース (thread.sleep) を消費するメソッド
ここに画像の説明を挿入します

2. 4つの非同期待機方法

1. 非同期制御待機コールバックコールバック

アクションの実行後、asyncResult と "nimen" がパラメータとしてコールバックに渡されます。
ここに画像の説明を挿入します

2. 非同期制御のwaiting-asyncResult.IsCompleted

ここに画像の説明を挿入します

3. 非同期制御は WaitOne() セマフォを待機します

ここに画像の説明を挿入します

4. 非同期制御はendinvokeを待ち、非同期関数の戻り値を取得します。

ここに画像の説明を挿入します
ここに画像の説明を挿入します

5.ビデオレッスンコード1

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace multithread
{
    
    
    public partial class Form1 : Form
    {
    
    
        public Form1()
        {
    
    
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
    
    
            Console.WriteLine($"[------------主线程start: 线程号:({
      
      Thread.CurrentThread.ManagedThreadId.ToString("00")})---{
      
      System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}-----]");

            /*1调用委托
            {
                Action<string> action = this.dosomething;
                action.Invoke("AA");//同步调用委托
                action("BB");//同步调用委托
                action.BeginInvoke("cc", null, null);//异步调用委托
            }
            

            /*2异步-不可控
            {
                Action<string> action = this.dosomething;
                for (int i = 0; i < 5; i++)
                {
                    string name = string.Format($"btn_click_{i}");
                    action.BeginInvoke(name, null, null);//异步调用委托
                }
             }*/

            /*3.异步不可控
            { 
               
                Action<string> action = this.dosomething;
                action.BeginInvoke("cc", null, null);
                Console.WriteLine($"[------------到这里计算完成: 线程号:({Thread.CurrentThread.ManagedThreadId.ToString("00")})---{System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}-----]");

            }*/


            /*4.异步等待1-回调
             {
                Action<string> action = this.dosomething;
                AsyncCallback callback = new AsyncCallback(i => Console.WriteLine($"[------------到这里计算完成: 线程号:({Thread.CurrentThread.ManagedThreadId.ToString("00")})---{System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}-----]"));
                //简单写法
                AsyncCallback callback1 = i => Console.WriteLine($"[------------到这里计算完成: 线程号:({Thread.CurrentThread.ManagedThreadId.ToString("00")})---{System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}-----]");
                action.BeginInvoke("cc", callback, null);
            }*/


            /*5.异步等待1-回调解析(action执行完将asyncResult、"nimen"当成参数传给callback)-不卡界面
            {
                Action<string> action = this.dosomething;
                    IAsyncResult asyncResult = null;
                    AsyncCallback callback = i =>
                    {
                        Console.WriteLine(object.ReferenceEquals(asyncResult , i));
                        Console.WriteLine(i.AsyncState);
                        Console.WriteLine($"[------------到这里计算完成: 线程号:({Thread.CurrentThread.ManagedThreadId.ToString("00")})---{System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}-----]");
                    };
                       asyncResult = action.BeginInvoke("cc", callback, "nimen");
                  }
          */

            /*6.异步等待2 asyncResult.IsCompleted-回调解析-while函数卡界面
             {
                 Action<string> action = this.dosomething;
                 IAsyncResult asyncResult = null;
                 AsyncCallback callback = i =>
                 {
                     Console.WriteLine(object.ReferenceEquals(asyncResult, i));
                     Console.WriteLine(i.AsyncState);
                     Console.WriteLine($"[------------到这里计算完成: 线程号:({Thread.CurrentThread.ManagedThreadId.ToString("00")})---{System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}-----]");
                 };
                 asyncResult = action.BeginInvoke("cc", callback, "nimen");

                 int a = 0;
                 while (!asyncResult.IsCompleted)
                 {

                     if (a < 10)
                     {
                         Console.WriteLine($"文件上传{a++ * 10}%..");
                         Console.WriteLine($"线程号:({Thread.CurrentThread.ManagedThreadId.ToString("00")})---{System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}");

                     }
                     else
                     {
                         Console.WriteLine($"文件上传99.9%..");
                     }
                     Thread.Sleep(200);
                 }

             }*/

            /*7.异步等待3-WaitOne()-信号量3
            {
                Action<string> action = this.dosomething;
                IAsyncResult asyncResult = action.BeginInvoke("cc", null, null);
                Console.WriteLine("dosomething");
                Console.WriteLine("dosomething");

                asyncResult.AsyncWaitHandle.WaitOne();//等待异步任务完成后,才打印计算完成
                asyncResult.AsyncWaitHandle.WaitOne(2000);//限时等待
                Console.WriteLine($"[------------到这里计算完成: 线程号:({Thread.CurrentThread.ManagedThreadId.ToString("00")})---{System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}-----]");
            }*/

            /*.8异步等待4endinvoke-拿到委托函数的返回值
            {
                Action<string> action = this.dosomething;
                Func<int> func = () =>
                {
                    Thread.Sleep(2000);
                    return DateTime.Now.Day;
                };
                Console.WriteLine($"func.Invoke()={ func.Invoke()}");
                IAsyncResult asyncResult=func.BeginInvoke(r =>
                {
                    Console.WriteLine(r.AsyncState);
                }, "nimen");
                Console.WriteLine($"func.EndInvoke={ func.EndInvoke(asyncResult)}");
                Console.WriteLine($"[------------到这里计算完成: 线程号:({Thread.CurrentThread.ManagedThreadId.ToString("00")})---{System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}-----]");
            }*/




            Console.WriteLine($"[------------主线程end: 线程号:({
      
      Thread.CurrentThread.ManagedThreadId.ToString("00")})---{
      
      System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}-----]");
        }




        private void dosomething(string name)
        {
    
    
            Console.WriteLine($"[****Dosomething {
      
      name} start: 线程号:({
      
      Thread.CurrentThread.ManagedThreadId.ToString("00")})---{
      
      System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}*******]");
            long result = 0;
            for(int i =0; i < 10000000; i++)
            {
    
    
                result += i;
            }
            Thread.Sleep(2000);

            Console.WriteLine($"[****Dosomething {
      
      name} end: 线程号:({
      
      Thread.CurrentThread.ManagedThreadId.ToString("00")})---{
      
      System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}{
      
      result}*******]");

        }

    }
}

3. .net1.0基本バージョンのスレッドオブジェクト

1. スレッドの起動

 {
    
    
                //Action<string> action = this.dosomething;
                //IAsyncResult asyncResult = action.BeginInvoke("cc", null, null);
                //Action action1 =()=> this.dosomething("CC");
                //1.线程启动 thread.Start();
                ThreadStart threadStart = () => this.dosomething("cc");
                Thread thread = new Thread(threadStart);
                thread.Start();
                //2.线等待程thread.Join();
                thread.Join(500);//卡主线程
                Console.WriteLine($"等待500ms");
                thread.Join();
                while (thread.ThreadState != ThreadState.Stopped)
                {
    
    
                    Thread.Sleep(100);//cpu时间片交出去干其他的事,但是内存还是占用
                }

ここに画像の説明を挿入します

2.thread.join()\thread.sleep()\thread.IsBackfround

thread.join(500), 他の人のスレッドは 500 ミリ秒待機しています。この時点で、プロジェクトには 2 つのスレッドがあります。1 つは 500 ミリ秒待機し、もう 1 つは thread.sleep(500) を実行しています
。 CPU タイム スライスを引き渡す独自のスレッド 他のことをするために外出しますが、メモリはまだ占有されています
フォアグラウンド スレッドは 500 ミリ秒スリープします ソフトウェアがクラッシュすると、ログが吐き出されます
ここに画像の説明を挿入します

4. ネット 2.0 スレッドプール

ここに画像の説明を挿入します

1. スレッドの起動とスレッド プール内のスレッドの最大数の設定

ここに画像の説明を挿入します
ここに画像の説明を挿入します

2.manualResetEvent.WaitOne() 非同期制御待機

            {
    
    
                ManualResetEvent manualResetEvent = new ManualResetEvent(false);
                ThreadPool.QueueUserWorkItem(t => 
                {
    
    
                    this.dosomething("cc");
                    manualResetEvent.Set();
                });//接收一个没有返回值的委托
                manualResetEvent.WaitOne();
                Console.WriteLine($"等待ThreadPool.QueueUserWorkItem执行完再执行");
            }

ここに画像の説明を挿入します
waitone() のリスクは、使用可能なスレッドがない場合にスタックしてしまうことです。
ここに画像の説明を挿入します

3. 手書きの非同期コールバック関数

ここに画像の説明を挿入します

4. 戻り値付きのデリゲート非同期呼び出し (フランス語の構造を学習)

ここに画像の説明を挿入します

5. .net3.0タスク

1.タスクスレッドの起動

ここに画像の説明を挿入します

2.waitall と waitany はメインスレッドインターフェイスをブロックします。

ここに画像の説明を挿入します
ここに画像の説明を挿入します
waitall、waitany アプリケーション シナリオ
ここに画像の説明を挿入します

3.task.whenall. continuewith()

まず、task.whenall はメイン インターフェイスでスタックせず、すぐに終了します。btn_task_click はメイン スレッド タスクです。
次に、 continuewith() です。tasklist 内のスレッドが実行され、条件が満たされると (all\any)、次のようになります。コミッションは「ドヤ顔」などのコールバックと同様に、順番に直接実行されます。
ここに画像の説明を挿入します
ここに画像の説明を挿入します

4.taskfactory. continuewith()

ここに画像の説明を挿入します
ここに画像の説明を挿入します

5. 最大 11 スレッドのみが動作するように設計されています

ここに画像の説明を挿入します

6. taskfactory は終了したスレッドを検出すると、スレッド ID を返します。

ここに画像の説明を挿入します
ここに画像の説明を挿入します

7. メインスレッドのブロックを避けるために、タスクを複数回ネストすることができます。

ここに画像の説明を挿入します
ここに画像の説明を挿入します

8. 2 つの waitall が順番に実行されます。

実行する必要のある 2 つの waitall があり、それらは異なるスレッドにありますが、2 つの waitall の順序を保証する必要がある場合、解決策は、最初の waitall をタスクリストに追加し、2 番目のタスクの waitall に判断させることです。 。
ここに画像の説明を挿入します

9.thread.sleep() スレッドはスタックしますが、 task.delay() はスレッドをスタックさせません

ここに画像の説明を挿入します
ここに画像の説明を挿入します

10.タスクスレッド完了識別

ここに画像の説明を挿入します

6. .net4.5パラレル

1.Parallel はマルチスレッドを開始します

ここに画像の説明を挿入します
ここに画像の説明を挿入します
ここに画像の説明を挿入します
ここに画像の説明を挿入します

2.平行ねじ停止

ここに画像の説明を挿入します

7. スレッドコア

1.例外処理

メインスレッドで例外が発生すると、プログラムを管理する人がいないとプログラムがクラッシュします。
ここに画像の説明を挿入します
ここに画像の説明を挿入します

  try {
    
    
                TaskFactory taskFactory = new TaskFactory();
                List < Task > tasklist = new List<Task>();
                //异常处理
                    for(int i =0;i<20; i++)
                    {
    
    
                        string name = string.Format($"btn_click_{
      
      i}");
                        Action<object> act = t =>
                        {
    
    
                            try
                            {
    
    
                                Thread.Sleep(2000);
                                if (t.ToString().Equals("btn_click_11"))
                                {
    
    
                                    throw new Exception(string.Format($"{
      
      t}执行失败"));
                                }

                                if (t.ToString().Equals("btn_click_12"))
                                {
    
    
                                    throw new Exception(string.Format($"{
      
      t}执行失败"));
                                }
                                Console.WriteLine("{0}执行成功", t);
                            }
                            catch (Exception ex)
                            {
    
    
                                Console.WriteLine(ex.Message);
                            }
                        };
                        tasklist.Add(taskFactory.StartNew(act, name));
                    };
                   Task.WaitAll(tasklist.ToArray());
            }
             catch (AggregateException aex)
            {
    
    
                foreach(var item in aex.InnerExceptions)
                {
    
    
                    Console.WriteLine(item.Message);
                }
            }
            catch(Exception ex)
            {
    
    
                Console.WriteLine(ex.Message);
            }
            

2. スレッドのキャンセル

ここに画像の説明を挿入します
tasklist.Add(taskFactory.StartNew(act, name)) 時、
ここに画像の説明を挿入します
tasklist.Add(taskFactory.StartNew(act, name,cts.Token)) 時、
ここに画像の説明を挿入します

 try {
    
    
                TaskFactory taskFactory = new TaskFactory();
                List < Task > tasklist = new List<Task>();
                CancellationTokenSource cts = new CancellationTokenSource();
                    for(int i =0;i<40; i++)
                    {
    
    
                        string name = string.Format($"btn_click_{
      
      i}");
                        Action<object> act = t =>
                        {
    
    
                            try
                            {
    
    
                            /*
                                if (cts.IsCancellationRequested)
                                {
                                    Console.WriteLine("{0}取消一个任务的执行",t);
                                }*/

                                Thread.Sleep(2000);
                                if (t.ToString().Equals("btn_click_11"))
                                {
    
    
                                    throw new Exception(string.Format($"{
      
      t}执行失败"));
                                }

                                if (t.ToString().Equals("btn_click_12"))
                                {
    
    
                                    throw new Exception(string.Format($"{
      
      t}执行失败"));
                                }
                                /*
                                else
                                {
                                    Console.WriteLine("{0}执行成功", t);
                                }*/
                                if (cts.IsCancellationRequested)
                                {
    
    
                                    Console.WriteLine("{0}放弃执行", t);
                                    return;
                                }
                                else
                                {
    
    
                                    Console.WriteLine("{0}执行成功", t);
                                }

                            }
                            catch (Exception ex)
                            {
    
    
                                cts.Cancel();
                                Console.WriteLine(ex.Message);
                            }
                        };
                        tasklist.Add(taskFactory.StartNew(act, name,cts.Token));
                    }
                Task.WaitAll(tasklist.ToArray());
            }
             catch (AggregateException aex)
            {
    
    
                foreach(var item in aex.InnerExceptions)
                {
    
    
                    Console.WriteLine(item.Message);
                }
            }
            catch(Exception ex)
            {
    
    
                Console.WriteLine(ex.Message);
            }

3. マルチスレッドの一時変数

ここに画像の説明を挿入します
ここに画像の説明を挿入します
ここに画像の説明を挿入します

4. スレッドセーフ ロック (ロックはリファレンスをロックします)

ここに画像の説明を挿入します

4.1 スレッドの安全性の問題

ここに画像の説明を挿入します
ここに画像の説明を挿入します
ここに画像の説明を挿入します

4.2 スレッド共有変数にはスレッド セーフティの問題がある

ここに画像の説明を挿入します

4.3ロック原理

1 つのスレッドのみが入ることができ、同時実行性がないため、問題は解決されますが、パフォーマンスが犠牲になります。Microsoft
ここに画像の説明を挿入します
ここに画像の説明を挿入します
ここに画像の説明を挿入します
定義のロックを使用することをお勧めします。
ここに画像の説明を挿入します

private static readonly object btn_click_lock = new object();//引用型变量
        private int TotalCount = 0;
        private List<int> IntList = new List<int>();
        private void button1_Click(object sender, EventArgs e)
        {
    
    
          Console.WriteLine($"[------------主线程start: 线程号:({
      
      Thread.CurrentThread.ManagedThreadId.ToString("00")})---{
      
      System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}-----]");

          TaskFactory taskFactory = new TaskFactory();
        //private static readonly object btn_click_lock = new object();


        List<Task> tasklist = new List<Task>();
            for (int i = 0; i< 10000; i++)
            {
    
    
                int newI = i;
                /*
                //非多线程
                this.TotalCount += 1;
                this.IntList.Add(newI);*/
                //多线程
                tasklist.Add(taskFactory.StartNew(() => 
                {
    
    
                   // int m = 3 + 2;
                   lock (btn_click_lock) 
                    {
    
     
                    this.TotalCount += 1;
                    this.IntList.Add(newI);
                    }

                }));

            }
            Task.WaitAll(tasklist.ToArray());
            Console.WriteLine(this.TotalCount);
            Console.WriteLine(this.IntList.Count());

         Console.WriteLine($"[------------主线程end: 线程号:({
      
      Thread.CurrentThread.ManagedThreadId.ToString("00")})---{
      
      System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}-----]");
        }

8、.net.0 await/async

ここに画像の説明を挿入します

1.待機原則

ここに画像の説明を挿入します
ここに画像の説明を挿入します

await の後のコードはコールバックに相当しますが、これは自分で記述したものではなく、コンパイラ ステート マシンに付属しています。
コールバックメソッド continuewith は子スレッドによって完了しますが、await は単なるコールバックではない (子スレッド、メインスレッド、またはコンピューターによって割り当てられる) ため、これらは異なります。

ここに画像の説明を挿入します

1.1 小規模コードのテストの原則

ここに画像の説明を挿入します

2. 戻り値のある非同期メソッドと戻り値のない非同期メソッド

2.1 のみ非同期

ここに画像の説明を挿入します

2.2asynic\await はペアで表示されます

await の後には戻り値のあるメソッドが続く必要があります
。async および await スイートを使用してメソッドを定義する場合、そのメソッドには戻り値がありませんが、このメソッドも戻り値があると見なすことができます。

戻り値を使用しないでください
ここに画像の説明を挿入します
戻り値を使用する
ここに画像の説明を挿入します

2.3 t.Wait() と await t の違い

ここに画像の説明を挿入します

2.4await 逆コンパイル - ステートマシンに相当

ここに画像の説明を挿入します
ここに画像の説明を挿入します

3. このナレッジポイントのコード

 private void button1_Click(object sender, EventArgs e)
        {
    
    
            testshow();
        }

        public static void testshow()
        {
    
    
            test();
        }

        private async static Task test()
        {
    
    
            Console.WriteLine($"当前主线程id={
      
      Thread.CurrentThread.ManagedThreadId.ToString("00")}---{
      
      System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}");
            {
    
    
                //NoReturnNoAwait();
                NoReturn();
                for (int i = 0; i < 10; i++)
                {
    
    
                    Thread.Sleep(300);
                    Console.WriteLine($"main thread task managedthreadid={
      
      Thread.CurrentThread.ManagedThreadId.ToString("00")}---{
      
      System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}--i={
      
      i}");
                }
            }
        }
        private static async void NoReturn()
        {
    
    
            //主线程执行
            Console.WriteLine($"NoReturn sleep before await ,threadid={
      
      Thread.CurrentThread.ManagedThreadId.ToString("00")}---{
      
      System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}");
            TaskFactory taskFactory = new TaskFactory();
            Task task = taskFactory.StartNew(() => 
            {
    
    
                Console.WriteLine($"NoReturn sleep before  ,threadid={
      
      Thread.CurrentThread.ManagedThreadId.ToString("00")}---{
      
      System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}");
                Thread.Sleep(300);
                Console.WriteLine($"NoReturn sleep after  ,threadid={
      
      Thread.CurrentThread.ManagedThreadId.ToString("00")}---{
      
      System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}");
            });
            await task;
            Console.WriteLine($"NoReturn sleep after await ,threadid={
      
      Thread.CurrentThread.ManagedThreadId.ToString("00")}---{
      
      System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}");

        }

        private static async void NoReturnNoAwait()
        {
    
                
            //主线程执行
            Console.WriteLine($"NoReturnNoAwait sleep before task ,threadid={
      
      Thread.CurrentThread.ManagedThreadId.ToString("00")}---{
      
      System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}");
            Task task = Task.Run(() =>
            {
    
    
                Console.WriteLine($"NoReturnNoAwait sleep before  ,threadid={
      
      Thread.CurrentThread.ManagedThreadId.ToString("00")}---{
      
      System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}");
                Thread.Sleep(300);
                Console.WriteLine($"NoReturnNoAwait sleep after  ,threadid={
      
      Thread.CurrentThread.ManagedThreadId.ToString("00")}---{
      
      System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}");
            });
            Console.WriteLine($"NoReturnNoAwait sleep after task ,threadid={
      
      Thread.CurrentThread.ManagedThreadId.ToString("00")}---{
      
      System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}");
        }

9. ダブルカラーボールプロジェクト

ここに画像の説明を挿入します
ここに画像の説明を挿入します
ここに画像の説明を挿入します
ここに画像の説明を挿入します
ここに画像の説明を挿入します

1.コード

知識のポイント
1. 7 つの独立したタスクにはマルチスレッド計算が必要です。7 つのスレッドを開始した後、スレッド メモリは計算を続けます。
2. 子スレッドはインターフェイスを更新できないため、this.Updatelbl(lbl, sNumber) を実装する必要があります。
3. while ( this.isgoon) から外部の 7 つのスレッドを終了します
。 4. ロック (llock) を使用して 7 つのサブスレッドをロックし、7 つのスレッドが同時に同じ値を持つのを防ぎます。 5. サブスレッドは待機し、
if (! this.isexsit(“00”) && !this.blue.Text.Equals(“00”))
6. すべてのサブスレッドの実行が終了したら、taskFactory.ContinueWhenAll(this.tasklist.ToArray(), t =) をコールバックします。 > this.ShowResult() );//

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace 双色球
{
    
    
    public partial class Form1 : Form
    {
    
    
        public Form1()
        {
    
    
            InitializeComponent();
            this.start.Enabled = true;
            this.stop.Enabled = false;
        }

        private string[] rednums = {
    
     
            "01","02","03","04","05","06","07","08","09","10",
            "11","12","13","14","15","16","17","18","19","20",
            "21","22","23","24","25","26","27","28","29","30",
            "31","32","33"
        };
        private string[] bluenums = {
    
    
            "01","02","03","04","05","06","07","08","09","10",
            "11","12","13","14","15","16"
        };
        private static readonly object llock =new object();
        private bool isgoon = true;
        private List<Task> tasklist = new List<Task>();
        private void start_Click(object sender, EventArgs e)
        {
    
    
            try
            {
    
    
                this.start.Text = "运行ing";
                this.start.Enabled = false;
                this.isgoon = true;
                this.tasklist = new List<Task>();
                this.blue.Text = "00";
                this.label1.Text = "00";
                this.label2.Text = "00";
                this.label3.Text = "00";
                this.label4.Text = "00";
                this.label5.Text = "00";
                this.label6.Text = "00";

                Console.WriteLine(new Random().Next(0, 15));
                Console.WriteLine(new Random().Next(0, 15));
                Console.WriteLine(new Random().Next(0, 15));
                Console.WriteLine(new Random().Next(0, 15));


                Thread.Sleep(1000);
                TaskFactory taskFactory = new TaskFactory(); 
                foreach(var control in this.groupBox1.Controls)
                {
    
    
                    if (control is Label)//标签中
                    {
    
    
                        Label lbl = (Label)control;
                        if (lbl.Name.Contains("blue")) //blue
                        {
    
    
                           tasklist.Add( taskFactory.StartNew(() =>
                            {
    
    
                                while (this.isgoon)
                                {
    
    
                                    int indexNum1 = Getrandombnumlong(0, bluenums.Length);
                                    string sNumber = this.bluenums[indexNum1];
                                    // lbl.Text = sNumber;
                                    this.Updatelbl(lbl, sNumber);
                                }
                            }));
                        }

                        else//red
                        {
    
    
                            tasklist.Add( taskFactory.StartNew(() =>
                            {
    
    
                                while (this.isgoon)
                                {
    
    
                                        int indexNum1 = Getrandombnumlong(0, this.rednums.Length);
                                        string sNumber = this.rednums[indexNum1];
                                    // lbl.Text = sNumber;
                                    lock (llock)
                                    {
    
    
                                        if (this.isexsit(sNumber))//加锁防止random出现一样的值
                                        {
    
    
                                            continue;//重复数据时放弃更新
                                        }
                                        this.Updatelbl(lbl, sNumber);//7个线程访问的是内存中同一个string sNumber,加锁防止取到的值是重复的,
                                    }
                                }
                            }));
                        }
                    }
                }

                //Thread.Sleep(3000);//直接等3000ms让stop开关enable,不靠谱,有可能有的线程因为数据重复,text没有更新
                //this.stop.Enabled = true;

                /*
                while (true)
                {
                    Thread.Sleep(1000);
                    if (!this.isexsit("00") && !this.blue.Text.Equals("00"))
                    {
                        this.stop.Enabled = true;
                        break;
                    }
                }*/

                Task.Run(()=> {
    
     
                   while (true)
                {
    
    
                    Thread.Sleep(1000);
                    if (!this.isexsit("00") && !this.blue.Text.Equals("00"))
                    {
    
    
                            this.Invoke(new Action(()=>{
    
    
                                this.stop.Enabled = true;//子线程操控不了主线程控件,需要主线程来完成
                            }));
                        
                        break;
                    }
                }

                });
                taskFactory.ContinueWhenAll(this.tasklist.ToArray(), t => this.ShowResult() );//所有子线程都执行完
            }
            catch(Exception ex)
            {
    
    
                Console.WriteLine("双色球启动出现异常:{0}",ex.Message);
            }
        }

        private void stop_Click(object sender, EventArgs e)
        {
    
    
            this.start.Enabled = true;
            this.stop.Enabled = false;
            this.isgoon = false;
            //Task.WaitAll(this.tasklist.ToArray());//主线程等着全部任务完成,子任务还需要主线程干活更新label,所以又是死锁
            //this.ShowResult();
        }

        private void ShowResult()
        {
    
    
            MessageBox.Show(string.Format("本期双色球为:{0} {1} {2} {3} {4} {5}   蓝球{6}"
                , this.label1.Text
                , this.label2.Text
                , this.label3.Text
                , this.label4.Text
                , this.label5.Text
                , this.label6.Text
                , this.blue.Text     ));
        }



        //更新界面
        private void Updatelbl(Label lbl ,string text)
        {
    
    
            if (lbl.InvokeRequired)
            {
    
    
                //这里的this指的是winform窗体主UI线程,让UI线程去更新text值
                this.Invoke(new Action(()=>{
    
    
                    //if (this.isgoon) //不延迟,点了就不更新
                   // { 
                    lbl.Text = text;
                    Console.WriteLine($"当前update线程id{
      
      Thread.CurrentThread.ManagedThreadId}");
                   // }
                }));
            }
            else
            {
    
    
                lbl.Text = text;
            }
        }

        //验证标签中的text是否有相同的值
        private bool isexsit(string snumber)
        {
    
    
            foreach (var control in this.groupBox1.Controls)
            {
    
    
                if (control is Label)//标签中的
                {
    
    
                    Label lbl = (Label)control;
                    if (lbl.Name.Contains("label"))
                    {
    
    
                        if (lbl.Text.Equals(snumber))
                        {
    
    
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        public int Getrandombnumlong(int min , int max)
        {
    
    
            int indexNum = new Random().Next(min, max);
            Thread.Sleep(1000);
            return indexNum;
        }


    }
}

ここに画像の説明を挿入します
ここに画像の説明を挿入します
ここに画像の説明を挿入します

10. ナレッジポイントコードの概要

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

namespace multithread
{
    
    
    public class Class1
    {
    
    

        private void dosomething(string name)
        {
    
    
            Console.WriteLine($"[****Dosomething {
      
      name} start: 线程号:({
      
      Thread.CurrentThread.ManagedThreadId.ToString("00")})---{
      
      System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}*******]");
        }

        //
        //Console.WriteLine($"[------------到这里计算完成: 线程号:({Thread.CurrentThread.ManagedThreadId.ToString("00")})---{System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}-----]");

        private void mainpoints() {
    
    
            //1委托调用
            Action<string> action = this.dosomething;//委托带参数
            Action action1 = () => this.dosomething("CC");//委托不带参数

            action.Invoke("AA");//同步调用委托
            action("BB");//同步调用委托
            action.BeginInvoke("cc", null, null);//异步调用委托
            for (int i = 0; i < 5; i++)
            {
    
    
                string name = string.Format($"btn_click_{
      
      i}");
                action.BeginInvoke(name, null, null);//异步多线程
            }

            List<Task> tasklist = new List<Task>();
            TaskFactory taskFactory = new TaskFactory();
            string name1 = string.Format($"btn_click");
            CancellationTokenSource cts = new CancellationTokenSource();
            Action<object> act = t =>
            {
    
    
                if (t.ToString().Equals("btn_click_11"))
                {
    
    
                    throw new Exception(string.Format($"{
      
      t}执行失败"));
                }
            };
            tasklist.Add(taskFactory.StartNew(act, name1, cts.Token));


            //2.委托执行完后进行回调
            //标准写法
            AsyncCallback callback = new AsyncCallback(i => Console.WriteLine($"[------------到这里计算完成: 线程号:({
      
      Thread.CurrentThread.ManagedThreadId.ToString("00")})---{
      
      System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}-----]"));
            //简单写法
            AsyncCallback callback1 = i =>
           {
    
    
               Console.WriteLine($"[------------到这里计算完成: 线程号:({
      
      Thread.CurrentThread.ManagedThreadId.ToString("00")})---{
      
      System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}-----]");
           };
            action.BeginInvoke("cc", callback, null);
            //回调1 -异步action执行完才会执行回调-action.BeginInvoke("cc", callback, "nimen")-1
            IAsyncResult asyncResult = null;
            asyncResult = action.BeginInvoke("cc", callback, "nimen");

            //回调1 - 异步action执行完才会执行回调 - action.BeginInvoke("cc", callback, "nimen")-2
            action.BeginInvoke("cc", r =>
            {
    
    
                Console.WriteLine("11");
            }, "nimen");

            //回调2 -异步action执行完才会执行回调-(!asyncResult.IsCompleted)
            while (!asyncResult.IsCompleted)
            {
    
    
                Console.WriteLine($"文件上传99.9%..");
            }
            //回调3 -异步action执行完才会执行回调--信号量WaitOne()
            asyncResult.AsyncWaitHandle.WaitOne();//等待异步任务完成后,才打印计算完成
            asyncResult.AsyncWaitHandle.WaitOne(2000);//限时等待

            // 回调4 - 异步action执行完才会执行回调 - EndInvoke拿到委托函数的返回值
            Func<int> func = () =>
            {
    
    
                return DateTime.Now.Day;
            };
            Console.WriteLine($"func.Invoke()={
      
       func.Invoke()}");
            IAsyncResult asyncResult1 = func.BeginInvoke(r =>
            {
    
    
                Console.WriteLine(r.AsyncState);
            }, "nimen");
            Console.WriteLine($"func.EndInvoke={
      
       func.EndInvoke(asyncResult1)}");

            //3.thread
            //3.1.线程启动 thread.Start();
            ThreadStart threadStart = () => this.dosomething("cc");
            Thread thread = new Thread(threadStart);
            thread.Start();


            //3.2.线等待程thread.Join();
            thread.Join(500);//卡主线程
            Console.WriteLine($"等待500ms");
            thread.Join();

            //3.3.thread.ThreadState /thread.IsBackfround
            while (thread.ThreadState != ThreadState.Stopped)
            {
    
    
                Thread.Sleep(100);//cpu时间片交出去干其他的事,但是内存还是占用
            }
            thread.IsBackground = true;//前台线程,当软件闪退时,会吐出日志

            //4.threadPool
            //4.1ThreadPool.GetMaxThreads
            ThreadPool.QueueUserWorkItem(
                            t =>
                           {
    
     this.dosomething("cc");
                               this.dosomething("dd");
                           });//接收一个没有返回值的委托
            ThreadPool.SetMaxThreads(16, 16);
            ThreadPool.GetMaxThreads(out int Workerthreads, out int completionPortThreads);
            Console.WriteLine($"Workerthreads:{
      
      Workerthreads}+completionPortThreads{
      
      completionPortThreads}");

            //4.2.manualResetEvent
            ManualResetEvent manualResetEvent = new ManualResetEvent(false);
            ThreadPool.QueueUserWorkItem(t =>
            {
    
    
                this.dosomething("cc");
                manualResetEvent.Set();
            });//接收一个没有返回值的委托
            manualResetEvent.WaitOne();
            Console.WriteLine($"等待ThreadPool.QueueUserWorkItem执行完再执行");

            //5.task
            Task.Run(() =>
                {
    
    
                    this.dosomething("cc");
                    Console.WriteLine($"等待ThreadPool.QueueUserWorkItem执行完再执行");
                });

            new Task(() => this.dosomething("33")).Start();

            TaskFactory taskFactory1 = Task.Factory;
            taskFactory.StartNew(() => this.dosomething("44"));

            //5.1 task.waitall、waitany  .ContinueWith taskFactory.ContinueWhenAny
            List<Task> tasklist1 = new List<Task>();
            tasklist.Add(Task.Run(() => this.dosomething("33")));
            tasklist.Add(Task.Run(() => this.dosomething("33")));
            Task.WaitAny(tasklist.ToArray());
            Task.WaitAll(tasklist.ToArray());
            Task.WhenAll(tasklist.ToArray()).ContinueWith(t =>
            {
    
    
                Console.WriteLine($"等待ThreadPool.QueueUserWorkItem执行完再执行");
            });

            taskFactory.ContinueWhenAny(tasklist.ToArray(), t => {
    
     });

            //5.2 task线程完成标识
            Task task = new Task(t => {
    
     }, "CC");
            Console.WriteLine(task.AsyncState);//会打印CC

            //5.3thread.sleep()卡线程 task.delay()不卡线程

            //5.4tasklist线程取消
            CancellationTokenSource cts1 = new CancellationTokenSource();
            tasklist.Add(taskFactory.StartNew(() => this.dosomething("44"), cts1.Token));


            //6.parallel启动多线程
            Parallel.Invoke(
                () => this.dosomething("11"),
                () => this.dosomething("22"),
                () => this.dosomething("33")
                );
            //6.1Parallel.For
            Parallel.For(0, 5, i => this.dosomething(i));
            Parallel.ForEach(new string[] {
    
     "0","1","2","3"},i=>this.dosomething("11"));

            //6.2控制并发数量
            ParallelOptions parallelOptions = new ParallelOptions();
            parallelOptions.MaxDegreeOfParallelism = 3;
            Parallel.For(0,40,parallelOptions,(i,state)=> {
    
     
                    if (i==2)
                {
    
    
                    state.Break();
                    state.Stop();
                }
            });

            //7.线程安全lock
            //private static readonly object btn_click = new object();
            int c = 0;
            tasklist.Add(taskFactory.StartNew(() => {
    
    
                int m = 3 + 2;
                lock (btn_click)
                {
    
    
                    c += 1;
                }
            
            }));

            //8.await/asyncawait 后的代码相当于回调,只不过不是自己写的,是编译器状态机自带的。
            //回调方法continuewith是子线程完成的,但await不仅仅是回调(可子线程可主线程,计算机分配),所以他们又有所不同
            private static async void Async()
            {
    
    
               await Task.Run(() => 
               {
    
    
                 Console.WriteLine($"等待ThreadPool.QueueUserWorkItem执行完再执行");
               });
                Console.WriteLine($"等待ThreadPool.QueueUserWorkItem执行完再执行");
            }

        }
    }



}

おすすめ

転載: blog.csdn.net/m0_46204224/article/details/131756149
おすすめ