1.创建线程

  1. using System;
  2. using System.Threading;
  3. namespace Threading {
  4. class Program {
  5. static void Main(string[] args) {
  6. Console.WriteLine("Hello World!");
  7. Thread t = new Thread(PrintNumbers);
  8. t.Start();
  9. PrintNumbers();
  10. Console.ReadLine();
  11. }
  12. static void PrintNumbers() {
  13. Console.WriteLine("Starting...");
  14. for (int i = 0; i < 10; i++) {
  15. Console.WriteLine(i);
  16. }
  17. }
  18. }
  19. }

2.线程休眠

Thread.Sleep 占用尽可能少的CPU时间

3.线程等待

Thread.Join();等待线程处于阻塞状态,工作线程执行完,主线程开始,工作线程执行期间主线程处于阻塞状态。
image.png

  1. using System;
  2. using System.Threading;
  3. namespace Threading {
  4. class Program {
  5. static void Main(string[] args) {
  6. Console.WriteLine("Hello World!");
  7. Thread t = new Thread(PrintNumbers);
  8. t.Start();
  9. t.Join();
  10. PrintNumbers();
  11. Console.ReadLine();
  12. }
  13. static void PrintNumbers() {
  14. Console.WriteLine("Starting...");
  15. for (int i = 0; i < 10; i++) {
  16. // Thread.Sleep(TimeSpan.FromSeconds(2));
  17. Console.WriteLine(i);
  18. }
  19. }
  20. }
  21. }

4.检测线程状态

CurrentThread.ThreadState.ToString()

using System;
using System.Threading;

namespace Threading {
    class Program {
        static void Main(string[] args) {
            Console.WriteLine("Hello World!");
            Thread t = new Thread(PrintNumbers);
            t.Start();
            Console.WriteLine(t.ThreadState.ToString());
            t.Join();
            Console.WriteLine(Thread.CurrentThread.ThreadState.ToString());
            PrintNumbers();
            Console.ReadLine();
        }

        static void PrintNumbers() {
            Console.WriteLine("Starting...");
            for (int i = 0; i < 10; i++) {
                // Thread.Sleep(TimeSpan.FromSeconds(2));
                Console.WriteLine(Thread.CurrentThread.ThreadState.ToString());          
                Console.WriteLine(i);
            }
        }
    }
}

5.线程优先级

执行第一个RunThreads(),几乎同一时间执行,设置单核运行后,计算耗时将超过2s,CPU核心大部分时间在运行高优先级线程,只留给剩下的线程很少的时间来运行。 Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1)

using System;
using System.Diagnostics;
using System.Runtime.InteropServices.ComTypes;
using System.Threading;

namespace Threading {
    class Program {
        static void Main(string[] args) {
            Console.WriteLine($"Current thread priority:{Thread.CurrentThread.Priority}");
            Console.WriteLine($"RUnning on all cores available");
            RunThreads();
            Thread.Sleep(TimeSpan.FromSeconds(2));
            Console.WriteLine("Running on a single core");
            Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1);//设置当前的进程的单核运行
            //表示关联进程内的线程可以在其上运行的处理器。 默认值取决于计算机上的处理器数。 默认值为 2 n - 1,其中 n 是处理器数。
            RunThreads();
            Console.ReadLine();
        }

        static void RunThreads() {
            var sample = new ThreadSample();
            var threadOne = new Thread(sample.CountNumbers);
            threadOne.Name = "ThreadOne";
            var threadTwo = new Thread(sample.CountNumbers);
            threadTwo.Name = "ThreadTwo";

            threadOne.Priority = ThreadPriority.Highest;
            threadTwo.Priority = ThreadPriority.Lowest;
            threadOne.Start();
            threadTwo.Start();

            Thread.Sleep(TimeSpan.FromSeconds(2));
            sample.Stop();
        }

    }

    public 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}");
        }
    }
}

6.前台线程和后台线程

设置线程二为后台线程,但前台线程结束后,后台线程立刻被终止。

using System;
using System.Diagnostics;
using System.Runtime.InteropServices.ComTypes;
using System.Threading;

namespace Threading {
    class Program {
        static void Main(string[] args) {
            var sampleForegroud = new ThreadSample(10);
            var sampleBackgroud = new ThreadSample(20);

            var threadOne = new Thread(sampleForegroud.CountNumbers);
            threadOne.Name = "ForegroudThread";

            var threadTwo = new Thread(sampleBackgroud.CountNumbers);
            threadTwo.Name = "BackgroudThread";
            threadTwo.IsBackground = true;//设置线程二为后台线程,但前台线程结束后,后台线程立刻被终止。

            threadOne.Start();
            threadTwo.Start();

        }



    }

    class ThreadSample {
        private readonly int _iterations;
        public ThreadSample(int iterations) {
            _iterations = iterations;
        }

        public void CountNumbers() {
            for (int i = 0; i < _iterations; i++) {
                Thread.Sleep(1);
                Console.WriteLine($"{Thread.CurrentThread.Name}print {i}");
            }
        }
    }
}

7.向线程传递参数

Thread.Start() 两个重载方法,可传递参数进入线程调用的函数中。

using System;
using System.Diagnostics;
using System.Runtime.InteropServices.ComTypes;
using System.Threading;

namespace Threading {
    class Program {
        static void Main(string[] args) {
            var sample = new ThreadSample(10);
            var threadOne = new Thread(sample.CountNumbers);
            threadOne.Start();
            threadOne.Join();//Join 一个同步方法,该方法阻止调用线程 (即,调用方法的线程) ,直到 Join 调用方法的线程完成。 
            //使用此方法可以确保线程已终止。 如果线程未终止,调用方将无限期阻止。

            Console.WriteLine("==========我是分割线=================");

            var threadTwo = new Thread(Count);
            threadTwo.Name = "ThreadTwo";
            threadTwo.Start(8);
            threadTwo.Join();

            Console.WriteLine("==========我是分割线=================");

            var threadThree = new Thread(() => CountNumbers(12));
            threadThree.Name = "ThreadThree";
            threadThree.Start();

        }

        static void Count(object iterations) {
            CountNumbers((int)iterations);
        }

        static void CountNumbers(int iterations) {
            for (int i = 0; i < iterations; i++) {
                Thread.Sleep(TimeSpan.FromSeconds(0.5));
                Console.WriteLine($"{Thread.CurrentThread.Name} prints {i}");
            }
        }

        static void PrintNumber(int number) {
            Console.WriteLine(number);
        }
    }

    class ThreadSample {
        private readonly int _iterations;
        public ThreadSample(int iterations) {
            _iterations = iterations;
        }

        public void CountNumbers() {
            for (int i = 0; i < _iterations; i++) {
                Thread.Sleep(1);
                Console.WriteLine($"{Thread.CurrentThread.Name}print {i}");
            }
        }
    }
}

8.使用死锁lock

8.1 为什么要lock,lock了什么?

当我们使用线程的时候,效率最高的方式当然是异步,即各个线程同时运行,其间不相互依赖和等待。但当不同的线程都需要访问某个资源的时候,就需要同步机制了,也就是说当对同一个资源进行读写的时候,我们要使该资源在同一时刻只能被一个线程操作,以确保每个操作都是有效即时的,也即保证其操作的原子性。lock是C#中最常用的同步方式,格式为lock(objectA){codeB} 。
lock(objectA){codeB} 看似简单,实际上有三个意思,这对于适当地使用它至关重要:
1. objectA被lock了吗?没有则由我来lock,否则一直等待,直至objectA被释放。
2. lock以后在执行codeB的期间其他线程不能调用codeB,也不能使用objectA。
3. 执行完codeB之后释放objectA,并且codeB可以被其他线程访问。

8.2 lock演示

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

namespace Lock {
    class Program {
        static void Main(string[] args) {
            var c = new Counter(); //多态
            Thread threadOne = new Thread(()=>TestCount(c));
            Thread threadTwo = new Thread(() => TestCount(c));
            Thread threadThree = new Thread(() => TestCount(c));
            threadOne.Start();
            threadTwo.Start();     
            threadThree.Start();
            threadOne.Join();
            threadTwo.Join();
            threadThree.Join();
            Console.WriteLine($"{c.Count}");
            Console.WriteLine($"This is lock example");

            var cL = new CounterWithLock();
            Thread threadFour = new Thread(() => TestCount(cL));
            Thread threadFive = new Thread(() => TestCount(cL));
            threadFour.Start();
            threadFive.Start();
            Console.WriteLine($"{cL.Count}");
            Console.ReadLine();
        }

        static void TestCount(CounterBase c) {
            for (int i = 0; i < 1000; i++) {
                c.Increment();
                c.Decrement();
            }
        }
    }

    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 {
        public int Count { get; private set; }
        private readonly object _syncRoot = new object();

        public override void Increment() {
            lock (_syncRoot) {//可能会造成性能问题,死锁。
                Count++;
            }
        }

        public override void Decrement() {
            lock (_syncRoot) {
                Count--;
            }
        }
    }
}

8.3 lock死锁演示

  ![image.png](https://cdn.nlark.com/yuque/0/2020/png/957395/1606045490570-6377b21a-ddae-4bd3-acb9-99ddb04cda0a.png#height=246&id=rwS8w&margin=%5Bobject%20Object%5D&name=image.png&originHeight=355&originWidth=490&originalType=binary&ratio=1&size=18261&status=done&style=none&width=340)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace Monitor {
    class Program {
        static void Main(string[] args) {
            var lock1 = new object();
            var lock2 = new object();
            new Thread(() => LockTooMuch(lock1, lock2)).Start();
            lock (lock2) {
                Console.WriteLine("lock2 Main");
                lock (lock1) {
                    Console.WriteLine("lock1 Main");
                }
            }
            Console.ReadLine();
        }

        static void LockTooMuch(object lock1,object lock2) {
            lock (lock1) {
                Thread.Sleep(3000);
                lock (lock2) {
                    Console.WriteLine("lock2 Thread");
                }
            }
        }
    }
}

9.使用Monitor类避免死锁

TryEnter()尝试进入,进不去则返回False

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

namespace MonitorExample {
    class Program {
        static void Main(string[] args) {
            var lock1 = new object();
            var lock2 = new object();
            new Thread(() => LockTooMuch(lock1, lock2)).Start();
            lock (lock2) {
                Console.WriteLine("lock2 Main");
                if (Monitor.TryEnter(lock1, TimeSpan.FromSeconds(5000))) {
                    Console.WriteLine("Access Successfully");
                }
                else {
                    Console.WriteLine("Access Unsuccessfully");
                }
            }
            Console.ReadLine();
        }

        static void LockTooMuch(object lock1,object lock2) {
            lock (lock1) {
                Thread.Sleep(3000);
                lock (lock2) {
                    Console.WriteLine("lock2 Thread");
                }
            }
        }
    }
}

10.TryCatch

.NETFramework(1.0/1.1)未被捕获的异常不会强制应用程序关闭。可以通过添加一个包含以下代码片段的应用程序配置文件(app.config)来使用该策略。

<configuration>
    <runtime>
      <legacyUnhandleExceptionPolicy enable="1"/>
  </runtime>
 </configuration>