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
方式1:
定义一个类,可以通过构造函数传入参数值
在方法中使用传入的值,创建的线程运行实例的方法

方式2:
在Start( )方法中传入参数值,方法强转为对应的类型

方式3
Lambda调用方法,直接传入参数

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,这值是随机变化的,不确定。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

flysh05

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值