做网站被抓,深圳网页设计公司推荐,设计建设网站公司网站,php购物网站开发摘要C#多线程开发详解 持续更新中。。。。。一、为什么要使用多线程开发1.提高性能2.响应性3.资源利用4.任务分解5.并行计算6.实时处理 二、多线程开发缺点1.竞态条件2.死锁和饥饿3.调试复杂性4.上下文切换开销5.线程安全性 三、多线程开发涉及的相关概念常用概念(1)lock(2)查看当前… C#多线程开发详解 持续更新中。。。。。一、为什么要使用多线程开发1.提高性能2.响应性3.资源利用4.任务分解5.并行计算6.实时处理 二、多线程开发缺点1.竞态条件2.死锁和饥饿3.调试复杂性4.上下文切换开销5.线程安全性 三、多线程开发涉及的相关概念常用概念(1)lock(2)查看当前工作线程信息(3)主线程、前台线程、后台线程 1.Thread线程(1)创建线程(2) 线程同步(3)线程异步 2.ThreadPool线程池3.Task任务1Task与Thead的关系 4.Task Parallel Library (TPL)任务并行库5.Async/Await异步/等待6.Monitor监视器7.Semaphore信号量8.SemaphoreSlim9.AutoResetEvent自动复位事件10.ManualResetEvent手动复位事件11.CancellationToken取消标记12.volatile易失性修饰符13.Mutex互斥锁14.ReaderWriterLock读写锁15.ReaderWriterLockSlim轻量级读写锁16.SpinLock17.SpinWait18.Barrier屏障 四、多线程的异常捕获问题 持续更新中。。。。。
一、为什么要使用多线程开发
1.提高性能 多线程允许程序同时执行多个任务从而有效利用多核处理器加快程序的执行速度。特别是在需要处理大量计算、I/O 操作或并行任务的应用中多线程可以显著提高性能。 2.响应性 多线程使应用能够同时处理多个用户请求或事件提高了应用的响应性。例如多线程可以保持用户界面的响应即使在执行长时间操作时也能让用户继续交互。 3.资源利用 多线程可以更有效地利用系统资源如内存和网络连接。这对于高并发服务器、网络应用和数据处理任务特别有用。 4.任务分解 将复杂任务分解为多个小任务每个任务在不同的线程中执行可以简化问题并提高可维护性。 5.并行计算 多线程可以用于并行计算例如在科学计算、数据分析和图像处理领域。这有助于加速大规模计算。 6.实时处理 在实时系统中多线程可以保证任务在规定的时间内完成从而满足对时间敏感性的需求。 二、多线程开发缺点
1.竞态条件 多线程可能会导致竞态条件即多个线程竞争访问共享资源可能导致数据不一致性和错误。 2.死锁和饥饿 不正确的线程同步可能导致死锁多个线程无法继续执行或饥饿某些线程无法获取所需资源问题。 线程12启动分别占用锁lock1lock2。之后线程1请求lock2但是线程2已经占用lock2线程1无法继续执行进入等待。线程2请求lock1但是线程1已经占用lock1线程2无法继续执行进入等待。这里陷入死锁线程1线程2都在等待对方释放锁来给自己使用程序一直无法运行一直在等待中。 using System;
using System.Threading;class DeadlockExample
{static object lock1 new object();static object lock2 new object();static void Main(){Thread thread1 new Thread(Method1);Thread thread2 new Thread(Method2);thread1.Start();thread2.Start();thread1.Join();thread2.Join();Console.WriteLine(Main thread finished.);}static void Method1(){lock (lock1){Console.WriteLine(Method1 acquired lock1.);Thread.Sleep(1000);Console.WriteLine(Method1 trying to acquire lock2.);lock (lock2){Console.WriteLine(Method1 acquired lock2.);}}}static void Method2(){lock (lock2){Console.WriteLine(Method2 acquired lock2.);Thread.Sleep(1000);Console.WriteLine(Method2 trying to acquire lock1.);lock (lock1){Console.WriteLine(Method2 acquired lock1.);}}}
}
3.调试复杂性 多线程程序的调试和错误跟踪可能会更加复杂因为线程间的交互和排错可能变得更难。 4.上下文切换开销 上下文切换Context Switching是多线程环境中的一种操作指的是在一个 CPU 核心上切换正在执行的线程从当前线程的执行上下文包括寄存器状态、程序计数器等切换到另一个线程的执行上下文, 线程的切换需要额外的开销因此在某些情况下过多的线程可能会导致性能下降。 当一个线程的时间片时间片轮转调度算法用完操作系统需要挂起该线程并切换到另一个线程。当一个线程主动放弃 CPU例如通过调用 Thread.Sleep()、Thread.Yield() 或等待某个事件时 3.当一个线程被高优先级的线程抢占 上下文切换的过程涉及以下步骤 保存当前线程的上下文: 操作系统将当前线程的寄存器状态、程序计数器等信息保存到该线程的内存空间中以便稍后能够恢复该线程的执行 2.恢复目标线程的上下文 操作系统从目标线程的内存空间中恢复寄存器状态、程序计数器等信息准备让目标线程继续执行。切换内核堆栈 每个线程都有自己的内核堆栈上下文切换时操作系统会切换内核堆栈以确保线程的隔离性。 上下文切换开销指的是从一个线程切换到另一个线程的过程中所涉及的时间和资源开销。这些开销主要包括以下几个方面 寄存器保存和恢复 当线程切换时操作系统需要保存当前线程的寄存器状态然后恢复目标线程的寄存器状态。这涉及到大量的数据拷贝和计算。 2.内存访问 上下文切换过程中需要频繁访问内存包括将寄存器状态和其他上下文信息写入内存以及从内存中读取目标线程的上下文信息。 3.调度开销 操作系统需要决定要切换到哪个线程这涉及到调度算法的开销包括选择合适的线程并进行必要的线程队列操作。 4.TLBTranslation Lookaside Buffer失效 当线程切换时虚拟内存的映射可能会发生变化导致 TLB 缓存失效从而增加了内存访问的开销。 上下文切换开销会影响系统的整体性能特别是在高并发、频繁切换的情况下。因此在设计多线程应用程序时需要考虑如何减少上下文切换的发生以提高程序的执行效率。一些方法包括 使用线程池线程池可以减少线程的创建和销毁从而减少上下文切换的频率。合理设置线程数量避免创建过多线程以减少不必要的上下文切换。 3.使用异步编程模型使用异步操作和任务可以减少线程的使用从而减少上下文切换。 5.线程安全性 多线程编程需要谨慎处理线程安全性以避免数据竞争和共享资源的冲突。 三、多线程开发涉及的相关概念
常用概念
(1)lock 在 C# 中lock 关键字用于实现线程同步以确保在多线程环境中对共享资源的访问是安全的。lock 关键字会创建一个互斥锁也称为监视器锁只有一个线程可以获得该锁从而确保在同一时间只有一个线程能够执行被 lock 包围的代码块。 lock (lockObject)
{// 在这里执行需要同步的代码
}其中lockObject 是一个用于同步的对象。它可以是任何引用类型的对象但通常是一个专门用于同步的对象。多个线程可以共享同一个 lockObject并且只有一个线程能够获得锁并执行被 lock 包围的代码块。 class Program
{static readonly object lockObject new object(); // 同步对象static void Main(string[] args){for (int i 0; i 5; i){ThreadStart start () {lock (lockObject){Console.WriteLine($Thread {Thread.CurrentThread.ManagedThreadId} is in the critical section.);Thread.Sleep(1000);Console.WriteLine($Thread {Thread.CurrentThread.ManagedThreadId} has exited the critical section.);}};Thread thread new Thread(start);thread.Start();}Console.ReadKey();}
} (2)查看当前工作线程信息 可以使用 Thread.CurrentThread 属性来获取当前正在执行的线程的信息。这个属性返回一个表示当前线程的 Thread 对象你可以使用它来查询线程的各种属性和状态。 Thread 类还提供了 Priority 属性允许你设置线程的优先级。然而操作系统不一定会完全遵循线程的优先级这取决于操作系统的调度机制。 线程可以分为前台线程和后台线程。前台线程是主线程的一部分如果所有前台线程都完成程序将终止。后台线程是在后台运行的线程如果所有前台线程都完成程序会立即终止不会等待后台线程完成。 using System;
using System.Threading;class Program
{static void Main(){Thread currentThread Thread.CurrentThread;Console.WriteLine($Thread ID: {currentThread.ManagedThreadId});Console.WriteLine($Thread Name: {currentThread.Name});Console.WriteLine($Is Thread Background: {currentThread.IsBackground});Console.WriteLine($Thread Priority: {currentThread.Priority});Console.WriteLine($Thread State: {currentThread.ThreadState});}
} (3)主线程、前台线程、后台线程 主线程Main Thread它是程序的入口点并且在程序启动时自动创建。主线程负责启动其他线程并且通常是其他线程的父线程但并不是所有线程都是主线程的子线程。 线程之间没有严格的父子关系。主线程和其他线程之间通常是平等的没有直接的父子关系。但是你可以通过编程来模拟一种线程间的层次关系使得某些线程在逻辑上看起来是其他线程的子线程。这通常涉及线程的创建、协调和通信 以下是一个示例演示了如何通过逻辑上的组织来模拟一种主线程和子线程的关系 using System;
using System.Threading;class Program
{static void Main(){Console.WriteLine(Main thread starts.);Thread parentThread new Thread(ParentThreadMethod);parentThread.Start();parentThread.Join();Console.WriteLine(Main thread ends.);}static void ParentThreadMethod(){Console.WriteLine(Parent thread starts.);Thread childThread new Thread(ChildThreadMethod);childThread.Start();childThread.Join();Console.WriteLine(Parent thread ends.);}static void ChildThreadMethod(){Console.WriteLine(Child thread starts.);Thread.Sleep(2000);Console.WriteLine(Child thread ends.);}
} 前台线程Foreground Threads 这些线程是由主线程或其他前台线程创建的它们的生命周期独立于主线程但它们不是主线程的子线程。前台线程与主线程之间的关系是平级的。当所有前台线程都执行完毕时程序才会退出无论主线程是否结束。 生命周期 前台线程的生命周期不受其他线程的影响。即使主线程退出前台线程仍然可以继续执行直到完成。程序退出 如果程序中还有前台线程在运行主程序将等待所有前台线程完成后才会退出。主线程也是前台线程如果主线程退出会等待其他前台线程完成后再退出。影响程序 前台线程会阻塞程序的退出直到所有前台线程完成。这可能会影响程序的退出速度。默认类型 通过 new Thread(…) 创建的线程默认是前台线程。 后台线程Background Threads 这些线程也是由主线程或其他前台线程创建的它们同样是平级的不是主线程的子线程。后台线程与主线程之间的关系也是平级的。当所有前台线程结束程序会退出同时会终止所有后台线程不管后台线程是否执行完毕。 生命周期 后台线程的生命周期受到主线程的影响。如果所有前台线程包括主线程都已经完成程序会立即退出同时终止后台线程不管后台线程是否执行完毕。程序退出 如果程序中只剩下后台线程在运行即使主线程结束程序也会立即退出不会等待后台线程完成。影响程序 后台线程不会阻塞程序的退出它们对程序的退出速度没有影响。设置后台线程 可以通过设置线程的 IsBackground 属性为 true 将线程设置为后台线程。通过 Thread 类创建的线程可以使用这个属性进行设置。 使用场景 前台线程通常用于执行一些关键任务确保这些任务的完成。例如在主线程需要等待其他线程的结果时可以使用前台线程。后台线程通常用于执行一些非关键性的任务如日志记录、监控等。它们不会阻止程序的退出适用于在程序退出时不需要保证任务完全执行的情况。 错误使用后台线程可能引起资源泄露或意外行为 资源泄露 如果后台线程在程序退出时还在执行可能会导致资源无法正确释放。例如如果后台线程打开了文件、网络连接或其他资源但程序退出时这些资源没有被正确关闭就会发生资源泄露。不完整的操作 如果后台线程执行一些需要完整执行的操作例如数据的写入、状态的更新等但程序退出时这些操作未完成可能会导致数据不一致或损坏。异常处理 后台线程的异常不会被捕获并传播到主线程可能会导致未处理的异常影响程序的稳定性。 4.线程同步 在程序退出时后台线程可能还在等待某些同步操作完成但这些操作可能无法在后台线程终止之前完成可能会导致死锁或其他线程同步问题。 1.Thread线程 表示一个执行线程用于并行执行代码。可以使用 Thread 类来创建和管理线程。线程是执行程序的最小单位多线程编程允许程序同时执行多个任务从而提高性能和响应性。 Thread 类是 C# 中用于线程操作的基础类之一。然而对于更高级的线程编程需求你可能会使用 Task、ThreadPool、异步编程模型等更高级的机制以便更好地管理和协调多线程操作。 Thead常用方法 Start() 启动线程使其开始执行指定的方法。Join() 阻塞当前线程直到目标线程完成。Abort() 强制终止线程的执行。不建议使用因为可能导致资源泄漏或不稳定的状态。Sleep(int millisecondsTimeout) 使当前线程休眠指定的毫秒数。IsAlive() 返回一个布尔值指示线程是否处于活动状态。Interrupt() 中断线程引发一个 ThreadInterruptedException 异常。Suspend() 和 Resume() 已过时不推荐使用。用于暂停和恢复线程的执行。GetDomain() 和 GetDomainID() 获取线程所属的应用程序域和域标识符。SetApartmentState(ApartmentState state) 设置线程的单元状态用于控制线程的COM互操作行为。GetCurrentThreadId() 和 GetDomainID() 获取当前线程的唯一标识符。Interrupt() 中断线程的等待状态引发 ThreadInterruptedException 异常。Yield() 提示系统允许其他等待线程运行。Name 和 CurrentThread.Name 获取或设置线程的名称。SetData 和 GetData 在线程范围内设置和获取线程本地存储数据。Start(ParameterizedThreadStart) 和 Start(ParameterizedThreadStart, Object) 启动线程并传递参数给线程方法。TrySetApartmentState(ApartmentState) 尝试设置线程的单元状态返回是否成功。StartNew(Action) 和 StartNew(Action, CancellationToken) 使用 Task 类来启动线程。 这些方法提供了各种线程管理和操作的能力。然而需要注意一些方法已经过时不推荐使用而且一些方法可能会涉及多线程编程的复杂性需要谨慎使用。在编写多线程应用程序时确保仔细阅读文档并根据需求选择适当的方法。 (1)创建线程 通常你需要传递一个方法作为线程的入口点然后调用 Start 方法来启动线程。 using System;
using System.Threading;class Program
{static void Main(){Thread thread new Thread(WorkerMethod);thread.Start(); // 启动线程}static void WorkerMethod(){Console.WriteLine(Thread is running.);}
}
(2) 线程同步 在多线程环境中线程同步是一种确保多个线程协调工作的机制。Thread 类提供了 Join 方法允许一个线程等待另一个线程完成。这在需要等待某个线程的结果时特别有用。 using System;
using System.Threading;class Program
{static void Main(){Thread currentThread Thread.CurrentThread;Console.WriteLine($Thread ID: {currentThread.ManagedThreadId});Thread thread new Thread(WorkerMethod);thread.Start();// 主线程等待子线程完成thread.Join();Console.WriteLine(Thread has finished.);}static void WorkerMethod(){Thread currentThread Thread.CurrentThread;Console.WriteLine($Thread ID: {currentThread.ManagedThreadId});Console.WriteLine(Thread is running.);Thread.Sleep(2000); // 模拟耗时操作}
} (3)线程异步
using System;
using System.Threading;class Program
{static void Main(){Thread currentThread Thread.CurrentThread;Console.WriteLine($Thread ID: {currentThread.ManagedThreadId});Thread thread new Thread(WorkerMethod);thread.Start();// 主线程等待子线程完成//thread.Join();Console.WriteLine(Thread has finished.);//这里子线程虽然还没有处理完但是直接返回了没有继续等待子线程但是子线程还在继续处理工作没有出现阻塞现象return ok;}static void WorkerMethod(){Thread currentThread Thread.CurrentThread;Console.WriteLine($Thread ID: {currentThread.ManagedThreadId});Console.WriteLine(Thread is running.);Thread.Sleep(10000); // 模拟耗时操作//这里在主线程结束后继续在处理10s后打印Thread is WordEndConsole.WriteLine(Thread is WordEnd.);}
}可以思考下主线程返回成功了但是子线程执行失败了这可怎么办 2.ThreadPool线程池 是一个用于管理和重用线程的机制可以使用 ThreadPool 类来执行异步任务。 3.Task任务 表示一个异步操作可以使用 Task 类或 Task.Run 方法来创建和管理任务。 1Task与Thead的关系
4.Task Parallel Library (TPL)任务并行库 是 C# 中用于并行编程的高级库用于处理异步和并行操作包括数据并行和任务并行。 5.Async/Await异步/等待 是 C# 5.0 引入的异步编程模型用于创建和管理异步方法和操作。 6.Monitor监视器 是用于实现线程同步的一种机制用于保护共享资源避免竞态条件。可以使用 Monitor 类或 lock 关键字来实现。 首先lock和Minitor有什么区别呢 其实lock在IL代码中会被翻译成Monitor。也就是Monitor.Enter(obj)和Monitor.Exit(obj). lockobj
{}//等价为
try{ Monitor.Enter(obj)
}catch
{}
finally
{Monitor.Exit(obj)
}7.Semaphore信号量 用于控制并发访问资源的数量可以使用 Semaphore 类来创建和管理信号量。 8.SemaphoreSlim 是 Semaphore 的改进版本提供更好的性能和可伸缩性。 9.AutoResetEvent自动复位事件 用于线程同步允许一个线程等待另一个线程发出信号。 10.ManualResetEvent手动复位事件 用于线程同步允许一个线程等待多个线程发出信号。 11.CancellationToken取消标记 用于在异步操作中请求取消操作可以在异步方法中传递给取消标记。 12.volatile易失性修饰符 用于标记字段指示编译器不应该对标记字段进行优化以确保多线程环境下的正确性。 13.Mutex互斥锁 是一种用于实现线程同步的机制用于保护共享资源防止多个线程同时访问。 14.ReaderWriterLock读写锁 允许多个线程同时读取共享资源但只允许一个线程写入资源。适用于读操作频繁、写操作较少的场景。 15.ReaderWriterLockSlim轻量级读写锁 是 ReaderWriterLock 的改进版本提供更好的性能和可伸缩性。 16.SpinLock 是一种自旋锁用于短时间内的临界区保护。它使用忙等待来尝试获取锁适用于临界区很小的情况。 17.SpinWait 用于在自旋等待期间执行自旋操作可以根据不同的条件进行自旋。 18.Barrier屏障 允许多个线程在一个点上等待直到所有线程都达到该点。适用于需要所有线程协调同步的场景。 四、多线程的异常捕获问题
相关文章