软件编程
位置:首页>> 软件编程>> C#编程>> c#多线程之线程基础

c#多线程之线程基础

作者:農碼一生  发布时间:2022-10-06 20:34:44 

标签:c#,多线程,线程,基础

一、简介

1.为了防止一个应用程序控制CPU而导致其他应用程序和操作系统本身永远被挂起这一可能情况,操作系统不得不使用某种方式将物理计算分割为一些虚拟的进程,并给予每个执行程序一定量的计算能力。此外操作系统必须始终能够优先访问CPU,并能调整不同程序访问CPU的优先级。线程正式这一慨念的实现。

2.多线程优缺点:
多线程优点:可以同时执行多个计算任务,有可能提高计算机的处理能力,使得计算机每秒能执行越来越多的命令
多线程缺点:消耗大量的操作系统资源。多个线程共享一个处理器将导致操作系统忙于管理这些线程,而无法运行程序。

二、创建线程

class Program
   {
       static void Main(string[] args)
       {
           Thread t1 = new Thread(new ThreadStart(PrintNumbers));//无参数的委托,把方法的引用当做参数
           t1.Start();

Thread t2 = new Thread(new ParameterizedThreadStart(PrintNumbers));//有参数的委托,把方法的引用当做参数
           t2.Start(10);
           Console.ReadLine();
       }

static void PrintNumbers()
       {
           Console.WriteLine("1.Starting...");
           for (int i = 0; i < 10; i++)
           {
               Console.WriteLine(i);
           }
       }

//注意:要使用ParameterizedThreadStart,定义的参数必须为object
       static void PrintNumbers(object count)
       {
           Console.WriteLine("2.Starting...");
           for (int i = 0; i < Convert.ToInt32(count); i++)
           {
               Console.WriteLine(i);
           }
       }
   }
}

注释:

1.我们只需指定在不同线程运行的方法名,而C#编译器会在后台创建这些对象。

2.要使用ParameterizedThreadStart,定义的参数必须为object类型。

三、暂停线程

class Program
   {
       static void Main(string[] args)
       {
           Thread t1 = new Thread(PrintNumbersWithDelay);
           t1.Start();
           PrintNumbers();
           Console.ReadLine();
       }

static void PrintNumbers()
       {
           Console.WriteLine("1.Starting...");
           for (int i = 0; i < 10; i++)
           {
               Console.WriteLine("In 1.Starting: " + i);
           }
       }

static void PrintNumbersWithDelay()
       {
           Console.WriteLine("2.Starting...");
           for (int i = 0; i < 10; i++)
           {
               //var a = TimeSpan.FromSeconds(2);
               Thread.Sleep(TimeSpan.FromSeconds(2));//暂停两秒
               Console.WriteLine("In 2.Starting: " + i);
           }
       }
   }

注释:使用Thread.Sleep(TimeSpan.FromSeconds(2));暂停线程一段时间

四、线程等待

class Program
   {
       static void Main(string[] args)
       {
           Console.WriteLine("Starting...");
           Thread t = new Thread(PrintNumbersWithDelay);
           t.Start();
           t.Join();   //使用Join等待t完成后,再向下执行PrintNumbers,如果注释掉输出明显不同
           PrintNumbers();
           Console.WriteLine("Thread Complete");
           Console.ReadLine();
       }

static void PrintNumbers()
       {
           Console.WriteLine("1.Starting...");
           for (int i = 0; i < 10; i++)
           {
               Console.WriteLine("In 1.Starting:" + i);
           }
       }

static void PrintNumbersWithDelay()
       {
           Console.WriteLine("2.Starting...");
           for (int i = 0; i < 10; i++)
           {
               Thread.Sleep(TimeSpan.FromSeconds(2));
               Console.WriteLine("In 2.Starting:" + i);
           }
       }
   }

注释:使用t.Join();   等待t完成。

五、终止线程

class Program
   {
       static void Main(string[] args)
       {
           Console.WriteLine("Starting Program...");
           Thread t1 = new Thread(PrintNumbersWithDelay);
           t1.Start();
           Thread.Sleep(TimeSpan.FromSeconds(7));//此时t1线程会执行7秒
           t1.Abort();    //使用Abort()终止线程
           Console.WriteLine("Thread t1 has been aborted");
           Thread t2 = new Thread(PrintNumbers);
           t2.Start();
           Console.ReadLine();
       }

static void PrintNumbers()
       {
           Console.WriteLine("1.Starting...");
           for (int i = 0; i < 10; i++)
           {
               Console.WriteLine("In 1.Starting:" + i);
           }
       }
       static void PrintNumbersWithDelay()
       {
           Console.WriteLine("2.Starting...");
           for (int i = 0; i < 10; i++)
           {
               Thread.Sleep(TimeSpan.FromSeconds(2));
               Console.WriteLine("In 2.Starting:" + i);
           }
       }
   }

注释:使用Thread实例的Abort方法终止线程。

六、检测线程状态

class Program
   {
       static void Main(string[] args)
       {
           Console.WriteLine("Start Program...");
           Thread t1 = new Thread(PrintNumbersWithStatus);
           Thread t2 = new Thread(DoNothing);
           Console.WriteLine("t1 status:" + t1.ThreadState.ToString());//获取实例线程状态
           t2.Start();
           t1.Start();
           for (int i = 0; i < 30; i++)
           {
               Console.WriteLine("t1 status:" + t1.ThreadState.ToString() + "\t" + "t2 status:" + t2.ThreadState.ToString());
           }
           Thread.Sleep(TimeSpan.FromSeconds(7));
           t1.Abort();
           Console.WriteLine("thread t1 has been aborted");
           Console.WriteLine("t1 status:" + t1.ThreadState.ToString());
           Console.WriteLine("t2 status:" + t2.ThreadState.ToString());
           Console.ReadLine();
       }

private static void PrintNumbersWithStatus()
       {
           Console.WriteLine("1.Starting...");
           Console.WriteLine("In 1.Starting t1 status:" + Thread.CurrentThread.ThreadState.ToString());//获取当前线程状态
           for (int i = 0; i < 10; i++)
           {
               Thread.Sleep(TimeSpan.FromSeconds(2));
               Console.WriteLine("In 1.Starting:" + i);
           }
       }

private static void DoNothing()
       {
           Thread.Sleep(TimeSpan.FromSeconds(2));
           Console.WriteLine("t2 Console...");
       }
   }

注释:使用Thread.ThreadState获取线程的运行状态。ThreadState是一个C#枚举。谨记:不要在程序中使用线程终止,否则可能会出现意想不到的结果

七、线程优先级

class Program
   {
       static void Main(string[] args)
       {
           //让操作系统的所有线程运行在多个CPU核心上
           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");
           //让操作系统的所有线程运行在单个CPU核心上
           Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1);
           RunThreads();
           Console.ReadLine();
       }

private static void RunThreads()
       {
           var sample = new ThreadSample();
           var t1 = new Thread(sample.CountNumbers);
           t1.Name = "Thread One";
           var t2 = new Thread(sample.CountNumbers);
           t2.Name = "Thread Two";

t1.Priority = ThreadPriority.Highest;//使用Priority设置线程的优先级
           t2.Priority = ThreadPriority.Lowest;
           t1.Start();
           t2.Start();//此处t2优先级低于t1,t2等待t1释放资源。

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

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} priority has a count={counter.ToString("N0")}");
       }
   }

注释:单核执行多线程耗费的时间比多核的多很多。

八、前台线程和后台线程

class Program
   {
       static void Main(string[] args)
       {
           var sampleForground = new ThreadSample(10);
           var sampleBackground = new ThreadSample(20);
           var t1 = new Thread(sampleForground.CountNumbers);//方法的引用
           t1.Name = "ForegroundThread"; //没有明确声明的均为前台线程

var t2 = new Thread(sampleBackground.CountNumbers);
           t2.Name = "BackgroundThread";
           t2.IsBackground = true;   //设置为后台线程

t1.Start();
           t2.Start();
           Console.ReadLine();

}
   }
   class ThreadSample
   {
       private readonly int _iteration;

public ThreadSample(int iteration)
       {
           _iteration = iteration;
       }

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

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

九、向线程传递参数

class Program
   {
       static void Main(string[] args)
       {
           ThreadSample sample = new ThreadSample(5);
           Thread t1 = new Thread(sample.CountNumbers);
           t1.Name = "ThreadOne";
           t1.Start();
           t1.Join();
           Console.WriteLine("--------------------------");

Thread t2 = new Thread(Count);
           t2.Name = "ThreadTwo";
           t2.Start(3);
           t2.Join();
           Console.WriteLine("--------------------------");

//使用lambda表达式引用另一个C#对方的方式被称为闭包。当在lambda表达式中使用任何局部变量时,C#会生成一个类,并将该变量作为该类的一个属性,但是我们无须定义该类,C#编译器会自动帮我们实现
           Thread t3 = new Thread(() => CountNumbers(5));
           t3.Name = "ThreadThree";
           t3.Start();
           t3.Join();
           Console.WriteLine("--------------------------");

int i = 10;
           Thread t4 = new Thread(() => PrintNumber(i));

i = 20;
           Thread t5 = new Thread(() => PrintNumber(i));
           t4.Start();
           t5.Start();
           //t4, t5都会输出20, 因为t4,t5没有Start之前i已经变成20了
           Console.ReadKey();
       }

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

static void CountNumbers(int iterations)
       {
           for (int i = 1; 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 _iteration;

public ThreadSample(int iteration)
       {
           _iteration = iteration;
       }

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

十、使用C# Lock 关键字

class Program
   {
       static void Main(string[] args)
       {
           Console.WriteLine("Incorrect Counter");
           Counter c1 = new Counter();
           var t1 = new Thread(() => TestCounter(c1));
           var t2 = new Thread(() => TestCounter(c1));
           var t3 = new Thread(() => TestCounter(c1));
           t1.Start();
           t2.Start();
           t3.Start();
           t1.Join();
           t2.Join();
           t3.Join();
           Console.WriteLine($"Total Count: {c1.Count}");
           Console.WriteLine("------------------------");

//使用LOCK关键字,Count同一时刻只允许一个线程访问
           Console.WriteLine("Correct counter");
           CounterWithLock c2 = new CounterWithLock();
           t1 = new Thread(() => TestCounter(c2));
           t2 = new Thread(() => TestCounter(c2));
           t3 = new Thread(() => TestCounter(c2));
           t1.Start();
           t2.Start();
           t3.Start();
           t1.Join();
           t2.Join();
           t3.Join();
           Console.WriteLine($"Total count:{c2.Count}");
           Console.ReadLine();
       }

static void TestCounter(CounterBase c)
       {
           for (int i = 0; i < 100000; i++)
           {
               c.Increment();
               c.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 _asyncRoot = new object();
           public int Count { get; private set; }
           //重写基类方法
           public override void Decrement()
           {
               lock (_asyncRoot)
               {
                   Count--;
               }
           }

public override void Increment()
           {
               lock (_asyncRoot)
               {
                   Count++;
               }
           }
       }

//基类
       abstract class CounterBase
       {
           public abstract void Increment();

public abstract void Decrement();
       }
   }
   class ThreadSample
   {
       private readonly int _iteration;

public ThreadSample(int iteration)//构造函数
       {
           _iteration = iteration;
       }

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

注释:不加锁,得出的结果不确定,竞争条件下很容易出错。加锁得出的结果是正确的,但是性能受到了影响

十一、使用Monitor类锁定资源

class Program
   {
       static void Main(string[] args)
       {
           object lock1 = new object();
           object lock2 = new object();
           new Thread(() => LockTooMuch(lock1, lock2)).Start();
           lock (lock2)
           {
               Thread.Sleep(1000);
               Console.WriteLine("Monitor.TryEnter allows not to get stuck, returning false after a specified timeout is elapsed");

//直接使用Monitor.TryEnter, 如果在第二个参数之前还未获取到lock保护的资源会返回false
               if (Monitor.TryEnter(lock1, TimeSpan.FromSeconds(5)))
               {
                   Console.WriteLine("Acquired a protected resource successfully");
               }
               else
               {
                   Console.WriteLine("Timeout acquiring a resource");
               }
           }
           new Thread(() => LockTooMuch(lock1, lock2)).Start();
           Console.WriteLine("-----------------------------");

/* 下面代码会造成死锁, 所以注释掉
           lock (lock2)
           {
               Console.WriteLine("This will be a deadlock!");
               Thread.Sleep(1000);
               lock (lock1)
               {
                   Console.WriteLine("Acquired a protected resource successfully");
               }
           }
           */
       }

static void LockTooMuch(object lock1, object lock2)
       {
           lock (lock1)
           {
               Thread.Sleep(1000);
               lock (lock2);
           }
       }
   }

注释:Monitor.TryEnter在指定的时间内尝试获取指定对象上的排他锁

十二、处理异常

class Program
   {
       static void Main(string[] args)
       {
           Thread t = new Thread(FaultyThread);
           t.Start();
           t.Join();
           try
           {
               t = new Thread(BadFaultyThread);
               t.Start();
           }
           catch (Exception ex)
           {
               Console.WriteLine("We won't get here");
           }
       }
       static void BadFaultyThread()
       {
           Console.WriteLine("Starting a bad faulty thread.....");
           Thread.Sleep(TimeSpan.FromSeconds(2));
           //这个异常主线程无法捕捉到,因为是在子线程抛出的异常。需要在子线程中加入try...catch捕获异常
           throw new Exception("Boom!");
       }
       static void FaultyThread()
       {
           try
           {
               Console.WriteLine("Starting a faulty thread...");
               Thread.Sleep(TimeSpan.FromSeconds(1));
               throw new Exception("Boom!");
           }
           catch (Exception ex)
           {
               Console.WriteLine($"Exception handled: {ex.Message}");
           }
       }
   }

来源:https://www.cnblogs.com/wml-it/p/14808097.html

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com