C#多线程基础(一 )

1. 使用Thread创建线程

创建一个Thread类的实例,传入要在独立线程执行的方法,启用Start()方法

最简单的示例如下:

using System.Threading;

定义一个方法

static void PrintNumbers()

        {

            Console.WriteLine("Print Numbers Starting....");

            for (int i = 1; i < 10; i++)

            {

                Console.WriteLine($"{i}->ThreadID:{Thread.CurrentThread.ManagedThreadId}");

            }

        }

在Main方法中测试:

static void Main(string[] args)

        {

            Console.WriteLine("Main Thread start...");

            Console.WriteLine($"MainID: {Thread.CurrentThread.ManagedThreadId}");

            Thread t = new Thread(PrintNumbers);

            t.Start();

            PrintNumbers();

            Console.Read();

        }

输出:

Main Thread start...

MainID: 1

Print Numbers Starting....

1->ThreadID:1

2->ThreadID:1

Print Numbers Starting....

1->ThreadID:3

2->ThreadID:3

3->ThreadID:3

4->ThreadID:3

5->ThreadID:3

6->ThreadID:3

7->ThreadID:3

8->ThreadID:3

9->ThreadID:3

3->ThreadID:1

4->ThreadID:1

5->ThreadID:1

6->ThreadID:1

7->ThreadID:1

8->ThreadID:1

9->ThreadID:1

从输出数据看,创建的线程(线程ID 3)和主线程(线程ID 3)同时在执行。

在方法中增加延时

static void PrintNumbersWithDelay()

        {

            Console.WriteLine("Starting....");

            for (int i = 1; i < 10; i++)

            {

                Thread.Sleep(TimeSpan.FromSeconds(0.2));

                Console.WriteLine($"{i}->ThreadID:{Thread.CurrentThread.ManagedThreadId}");

            }

        }

再在Main函数中执行测试:

static void Main(string[] args)

        {

            Console.WriteLine("Main Thread start...");

            Console.WriteLine($"MainID: {Thread.CurrentThread.ManagedThreadId}");

            Thread t = new Thread(PrintNumbersWithDelay);

            t.Start();

            PrintNumbersWithDelay();

            Console.Read();

        }

输出:

Main Thread start...

MainID: 1

Starting....

Starting....

1->ThreadID:3

1->ThreadID:1

2->ThreadID:3

2->ThreadID:1

3->ThreadID:3

3->ThreadID:1

4->ThreadID:3

4->ThreadID:1

5->ThreadID:3

5->ThreadID:1

6->ThreadID:3

6->ThreadID:1

7->ThreadID:3

7->ThreadID:1

8->ThreadID:3

8->ThreadID:1

9->ThreadID:3

9->ThreadID:1

2个线程同时都在执行打印输出,每个电脑执行的结果可能不完全相同,只是都能反应并行输出。

2. 线程暂停Sleep()方法,线程等待Join()方法

等待线程完成后所输出的结果

例如,计算从1加到100

static void Sum()

        {

            //int result = 0;

            Console.WriteLine("Starting....");

            for (int i = 0; i <= 100; i++)

            {

                result += i;

            }

        }

在Main函数中测试验证:

    private static int result = 0;

        static void Main(string[] args)

        {

            Thread t = new Thread(Sum);

            t.Start();

            //Thread.Sleep(1000);

            Console.WriteLine(result);

            t.Join();

            Console.WriteLine($"Complteted:{result}");

        }

输出:

Starting....

0 //线程开始后,这个值还是0,如果休眠1秒中,情况就不一样了,在主线休眠1秒的时间,独立线程以及计算完成,所以也是5050

Complteted:5050 //等待线程执行完成后的结果

3. 线程优先级

设置线程的ThreadPriority属性

创建一个ThreadSample类,存在一个不停计数的方法和一个停止计数的布尔变量,将类的实例布尔变量设置为false时,停止计数。

class ThreadSample

        {

            private bool _isStopped = false;

            public void  Stop()

            {

                _isStopped = true;

            }

            public void CountNumbers()

            {

                long counter = 0;

                while(!_isStopped)

                {

                    counter++;

                }

                Console.WriteLine($"{Thread.CurrentThread.Name} with {Thread.CurrentThread.Priority,11} has a count{counter.ToString("N0"),13}");

            }

        }

创建一个静态方法来测试线程优先级:

static void RunThreads()

        {

            var sample = new ThreadSample();

            var t1 = new Thread(sample.CountNumbers);

            t1.Name = "线程1";

            var t2 = new Thread(sample.CountNumbers);

            t2.Name = "线程2";

            t1.Priority = ThreadPriority.Highest;

            t2.Priority = ThreadPriority.Lowest;

            t1.Start();

            t2.Start();

            Thread.Sleep(2000);

            sample.Stop();

        }

不同的线程优先级,是否计数一样多呢?

Main Thread priority Normal

Running on all cores available

线程1 with    Highest has a count  897,909,334

线程2 with      Lowest has a count  689,778,768

显然时高优先级执行的迭代次数多,但总体时一致的。

如果使用单核测试,情况就不一样了。

Console.WriteLine("Run on a single core");

Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(10);

 RunThreads();

输出:

Run on a single core

线程2 with      Lowest has a count  838,668,519

线程1 with    Highest has a count1,024,629,110

显然不时一个数量级

4. 终止线程,线程的状态

Abort() 方法:该技术不一定总能终止线程,不推荐使用该方法来关闭线程

ThreadState属性值即为线程的状态

测试代码如下:

static void DoNothing()

        {

            Console.WriteLine("T2 Starting...");

            Thread.Sleep(2000);

        }

        static void PrintNumbersWithStatus()

        {

            Console.WriteLine($"{Thread.CurrentThread.Name}:{Thread.CurrentThread.ThreadState.ToString()}");

            for (int i = 1; i < 10; i++)

            {

                Thread.Sleep(500);

                Console.WriteLine(i);

            }

        }

static void Main(string[] args)

        {

            Console.WriteLine("Main Starting ....");

            Thread t = new Thread(PrintNumbersWithStatus);

            t.Name = "T1 withs status";

            Thread t2 = new Thread(DoNothing);

            t2.Name = "T2 do nothing";

            Console.WriteLine($"主线程状态:{Thread.CurrentThread.ThreadState.ToString()}");

            Console.WriteLine($"在主线程中 T1线程状态:{t.ThreadState.ToString()}");

            Console.WriteLine($"在主线程中 T2程状态:{t2.ThreadState.ToString()}");

            t2.Start();

            t.Start();

            for (int i = 1; i < 30; i++)

            {

                Console.WriteLine($"T1:{t.ThreadState.ToString()}");

            }

            Thread.Sleep(6000);

            t.Abort();

            Console.WriteLine("T1 Thread has been aborted");

            Console.WriteLine($"在主线程中 T1线程状态:{t.ThreadState.ToString()}");

            Console.WriteLine($"在主线程中 T2程状态:{t2.ThreadState.ToString()}");

            Console.WriteLine($"主线程状态:{Thread.CurrentThread.ThreadState.ToString()}");

            Console.ReadLine();

        }

输出:

Main Starting ....

主线程状态:Running

在主线程中 T1线程状态:Unstarted

在主线程中 T2程状态:Unstarted

T2 Starting...

T1:Running

T1:Running

T1:Running

T1:Running

T1:Running

T1:Running

T1 withs status:Running

T1:Running

T1:WaitSleepJoin

T1:WaitSleepJoin

T1:WaitSleepJoin

T1:WaitSleepJoin

T1:WaitSleepJoin

T1:WaitSleepJoin

T1:WaitSleepJoin

T1:WaitSleepJoin

T1:WaitSleepJoin

T1:WaitSleepJoin

T1:WaitSleepJoin

T1:WaitSleepJoin

T1:WaitSleepJoin

T1:WaitSleepJoin

1

2

3

4

5

6

7

8

9

T1 Thread has been aborted

在主线程中 T1线程状态:Aborted

在主线程中 T2程状态:Stopped

主线程状态:Running

5.前台线程和后台线程

显示创建的线程都是前台线程

可通过设置 IsBackground属性设置true来创建后台线程

主要区别:

进程会等待所有前台线程完成后再结束工作,如果只剩下后台线程,则会直接结束工作。

如果程序定义了一个不会完成的前台线程,主程序就不会正常结束。

创建如下示例代码:

class ThreadSample

        {

            private readonly int _iterations;

            public ThreadSample(int iterations)

            {

                _iterations = iterations;

            }

            public void CountNumbers()

            {

                for (int i = 0; i < _iterations; i++)

                {

                    Thread.Sleep(500);

                    Console.WriteLine($"{Thread.CurrentThread.Name} prints {i}");

                }

            }

        }


static void Main(string[] args)

        {

            var TForegroundSample = new ThreadSample(10);

            var TBackgroundSample = new ThreadSample(20);

            var t1 = new Thread(TForegroundSample.CountNumbers);

            t1.Name = "ForegroundThread";

            var t2 = new Thread(TBackgroundSample.CountNumbers);

            t2.Name = "BackgroundThread";

            t2.IsBackground = true;

            t1.Start();

            t2.Start();

            //Console.Read();

        }

2给线程同时执行,但是迭代到9时,前台线程完成,虽然有后线程在执行,所有的前台线程执行完成,不会等待后台线程执行完成的,就直接结束了整个主线程。

6. 向线程传递参数

创建示例代码:

class Demo

        {

            private readonly int _iterations;

            public Demo(int iterations)

            {

                _iterations = iterations;

            }

            public void CountNumbers()

            {

                for (int i = 0;  i<=_iterations; i++)

                {

                    Thread.Sleep(500);

                    Console.WriteLine($"{Thread.CurrentThread.Name} prints {i}");

                }

            }

        }

static void CountNumbers(int iterations)

        {

            for (int i = 0; i <= iterations; i++)

            {

                Thread.Sleep(500);

                Console.WriteLine($"{Thread.CurrentThread.Name} prints {i}");

            }

        }

static void Count(object iteratios)

        {

            CountNumbers((int)iteratios);

        }

static void PrintNUmbers(int num)

        {

            Console.WriteLine(num);

        }

static void Main(string[] args)

        {

        //创建一个演示类的实例,使用构成函数传入值

         var Sample = new Demo(10);

            var t1 = new Thread(Sample.CountNumbers); //创建线程执行实例的方法

            t1.Start();

            t1.Join();

            Console.WriteLine("---------------------------------");

            var t2 = new Thread(Count); //创建现在执行静态方法,参数为Object

            t2.Name = "T2";

            t2.Start(8); //传入参数,函数会强转为int

            t2.Join();

            Console.WriteLine("---------------------------------");

            var t3 = new Thread(() => CountNumbers(12)); //使用Lambda表达式

            t3.Name = "T3";

            t3.Start();

            t3.Join();

            Console.WriteLine("---------------------------------");

            int i = 10;

            var t4 = new Thread(() => PrintNUmbers(i));

            i = 20;

            var t5 = new Thread(() => PrintNUmbers(i)); //线程4和5都会打印20,启动线程前变量被修改为20

            t4.Start();

            t5.Start();

        }

输出:

prints 0

prints 1

prints 2

prints 3

prints 4

prints 5

prints 6

prints 7

prints 8

prints 9

prints 10

---------------------------------

T2 prints 0

T2 prints 1

T2 prints 2

T2 prints 3

T2 prints 4

T2 prints 5

T2 prints 6

T2 prints 7

T2 prints 8

---------------------------------

T3 prints 0

T3 prints 1

T3 prints 2

T3 prints 3

T3 prints 4

T3 prints 5

T3 prints 6

T3 prints 7

T3 prints 8

T3 prints 9

T3 prints 10

T3 prints 11

T3 prints 12

---------------------------------

20

20

7.线程加锁

确保当一个线程使用某些资源时,同时其它线程无法使用该资源

确保线程安全

创建2个类,一个加锁,一个不加锁:

abstract class CounterBase

        {

            public abstract void Increment();

            public abstract void Decrement();

        }

        class Counter : CounterBase

        {

            public int Count { get; private set; }

            public override void Decrement()

            {

                Count--;

            }

            public override void Increment()

            {

                Count++;

            }

        }

        class CounterWithLock : CounterBase

        {

            private readonly object obj_lock = new object();

            public int Count { get; private set; }

            public override void Decrement()

            {

                lock (obj_lock)

                {

                    Count--;

                }

            }

            public override void Increment()

            {

                lock (obj_lock)

                {

                    Count++;

                }

            }

        }

        static void TestCounter(CounterBase c)

        {

            for (int i = 0; i < 100000; i++)

            {

                c.Increment();

                c.Decrement();

            }

        }

在Main方法中验证:

static void Main(string[] args)

        {

            Console.WriteLine("Incorrect Counter");

            var c = new Counter();

            var t1 = new Thread(() => TestCounter(c));

            var t2 = new Thread(() => TestCounter(c));

            var t3 = new Thread(() => TestCounter(c));

            t1.Start();

            t2.Start();

            t3.Start();

            t1.Join();

            t2.Join();

            t3.Join();

            Console.WriteLine($"Total Count:{c.Count}");

            Console.WriteLine("------------------------------");

            Console.WriteLine("Correct Counter");

            var c1 = new CounterWithLock();

            t1 = new Thread(() => TestCounter(c));

            t2 = new Thread(() => TestCounter(c));

            t3 = new Thread(() => TestCounter(c));

            t1.Start();

            t2.Start();

            t3.Start();

            t1.Join();

            t2.Join();

            t3.Join();

            Console.WriteLine($"Total Count:{c1.Count}");

            Console.WriteLine("------------------------------");

        }

输出:

Incorrect Counter

Total Count:122

------------------------------

Correct Counter

Total Count:0

------------------------------

从结果可以看到出,加锁后输出和预期的一样,执行一次自增,再执行一次自减,理论是0.

但是不加锁的执行结果是122,这值是随机变化的,不确定。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,128评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,316评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,737评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,283评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,384评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,458评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,467评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,251评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,688评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,980评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,155评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,818评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,492评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,142评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,382评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,020评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,044评论 2 352

推荐阅读更多精彩内容

  • C#多线程(14):任务基础②代码有点多,不易观察,请复制到程序中运行。 class Program { ...
    金色888阅读 147评论 0 0
  • 多个线程同时使用共享对象,这种情形被称为竞争条件(Race Condition),竞争条件是多线程环境中非常常见的...
    LH_晴阅读 3,426评论 0 2
  • 简易线程控制方法: 新建线程,无返回值,不带参 Thread thread = new Thread(new Th...
    铸剑悄悄对你说阅读 853评论 1 0
  • 我是黑夜里大雨纷飞的人啊 1 “又到一年六月,有人笑有人哭,有人欢乐有人忧愁,有人惊喜有人失落,有的觉得收获满满有...
    陌忘宇阅读 8,532评论 28 53
  • 首先介绍下自己的背景: 我11年左右入市到现在,也差不多有4年时间,看过一些关于股票投资的书籍,对于巴菲特等股神的...
    瞎投资阅读 5,721评论 3 8