首页 > 其他分享 >并发锁与线程池(一)

并发锁与线程池(一)

时间:2024-03-27 19:30:57浏览次数:16  
标签:并发 互斥 使用 访问 线程 自旋

1.什么是线程?

线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

在多核或多CPU系统中,线程可以被调度到不同的核心上执行,从而实现真正的并行计算。而在单核系统中,线程通过时间分片(time slicing)的方式进行并发执行,操作系统会快速地在多个线程之间切换,给人一种同时执行的错觉。

  • 线程的优势在于:
    提高程序响应:在图形用户界面(GUI)程序中,线程可以使得用户界面在处理其他任务时仍然能够响应用户的操作。
    提高计算机资源的利用率:线程可以使得多CPU系统得到更好的利用。
    简化程序结构:通过使用线程,可以简化对某些问题的处理,例如生产者-消费者问题。
    然而,多线程编程也引入了复杂性,如同步问题、死锁和竞态条件等。因此,在进行多线程编程时,需要仔细设计并管理线程之间的交互。

2.为什么引入并发锁

并发锁(Concurrency lock),也称为互斥锁(Mutex)、同步锁或线程锁,是在多线程编程中用来控制对共享资源(如数据结构、文件、内存等)的访问的机制。引入并发锁的主要目的是为了防止多个线程同时修改同一资源,导致数据不一致或竞态条件(Race Condition)的问题。
以下是引入并发锁的几个主要原因:

  1. 防止竞态条件:当两个或多个线程同时访问同一数据,并且至少有一个线程对数据进行写操作时,如果没有适当的同步机制,就可能会发生竞态条件。使用并发锁可以确保在同一时间只有一个线程能够访问共享资源,从而避免竞态条件。
  2. 保持数据一致性:在多线程环境中,如果没有锁的保护,多个线程可能会同时修改同一数据,导致数据处于不一致的状态。锁可以确保对共享资源的所有访问都是序列化的,从而保持数据的一致性。
  3. 控制执行顺序:在某些情况下,可能需要控制线程的执行顺序,或者确保某些操作按照特定的顺序执行。锁可以帮助实现这种控制,通过锁的获取和释放来协调线程的执行。
  4. 避免死锁:虽然锁可以解决多线程同步问题,但如果使用不当,也可能导致死锁,即两个或多个线程永久地等待对方释放锁。因此,正确地使用锁和设计锁的策略是避免死锁的关键。
  5. 提高程序的正确性:在并发编程中,正确地使用锁可以保证程序的正确性,避免由于并发访问导致的不可预测的结果。

总之,并发锁是多线程编程中保证数据同步和避免竞态条件的重要工具。正确地使用锁可以保证程序的正确性、可靠性和效率。然而,过度使用锁或者不当地使用锁可能会导致性能下降或死锁等问题,因此需要谨慎地设计和实现锁策略。

3.多核处理器上运行线程需要锁吗?

在多核处理器上运行的线程确实可能需要使用并发锁,具体取决于它们如何访问和修改共享资源。以下是几种情况,其中并发锁可能是必要的:

  1. 共享资源:当多个线程需要访问和修改相同的资源时,例如一个共享的数据结构或变量,并发锁可以确保一次只有一个线程能够执行关键部分,从而防止竞态条件和数据不一致的问题。
  2. 同步操作:在某些情况下,需要确保操作的顺序或者某些操作的全局顺序,这时候使用锁可以提供这种同步,确保线程按照预期的方式执行。
  3. 互斥访问:如果某些资源不能同时被多个线程访问(例如,一个物理设备或一个文件),则锁可以用来保证互斥访问。

然而,使用并发锁也会带来一些潜在的问题,比如死锁、降低并发性和增加复杂性。因此,在设计多核系统上的并发程序时,应该考虑以下替代方案或补充措施:

  1. 无锁编程:使用原子操作和内存顺序保证来避免使用锁,这可以提高并发性能,但需要更高级的编程技术。
  2. 数据局部性:通过设计数据结构来减少共享,增加数据的局部性,从而减少对锁的需求。
  3. 消息传递:使用消息传递而不是共享内存来同步线程,这可以在某些情况下避免使用锁。
  4. 软件事务内存(STM):这是一种编程模型,它允许线程对共享内存进行事务性的访问,从而避免了传统的锁机制。
  5. 并发数据结构:使用专门设计的数据结构,如并发队列、并发哈希表等,这些数据结构内部处理了同步问题,对使用者透明。
    总之,是否使用并发锁取决于具体的应用场景和需求。在多核处理器上,合理地使用锁可以保证线程安全,但也应该探索其他可能的并发控制机制,以优化性能和可伸缩性。

接下来我们看看有哪些

  • 互斥锁:

锁定(Locking):当一个线程想要访问一个共享资源时,它必须先获取关联该资源的互斥锁。如果互斥锁当前是解锁状态,线程会成功获取锁,并将锁设置为锁定状态。如果互斥锁已经是锁定状态,表明有其他线程正在访问该资源,线程就会进入等待状态,直到互斥锁被释放。

解锁(Unlocking):当线程完成对共享资源的访问后,它必须释放互斥锁,使得其他等待的线程可以获取锁并访问资源。

在 C 语言中,使用 POSIX 线程库(pthread)提供的函数来创建和使用互斥锁,如 pthread_mutex_init 初始化互斥锁,pthread_mutex_lock 尝试获取互斥锁,pthread_mutex_unlock 释放互斥锁等。

  • 自旋锁:

当一个线程尝试获取一个已被其他线程获取的锁时,该线程将在一个循环中不断地检查锁是否已经可用,而不是立即放弃CPU的执行权。这种循环检查锁的状态的过程被称为“自旋”。
自旋锁的特点和用途如下:

  • 忙等待:自旋锁的一个主要特点是它采用了忙等待(busy-waiting)的策略。这意味着线程在等待锁释放的过程中,会一直占用CPU资源,不断检查锁的状态。
    适用场景:自旋锁适用于锁只会被持有很短时间的情况,因为在锁被持有的时间较短时,忙等待的开销远小于线程上下文切换的开销。

  • 低延迟:由于自旋锁避免了线程状态切换的开销,它在锁被占用时间短的情况下可以提供较低的延迟。
    不适用于长时间等待:如果锁被持有的时间较长,自旋锁会浪费大量的CPU资源,因为等待的线程会一直在循环中自旋,而不是进行其他有用的计算。

自旋锁是一种高效的同步机制,但它的使用需要谨慎,因为它可能会引起性能问题,特别是当锁被长时间持有时。因此,在设计系统时,应该根据具体的应用场景和性能要求来选择是否使用自旋锁。

标签:并发,互斥,使用,访问,线程,自旋
From: https://blog.csdn.net/m0_74973115/article/details/137080210

相关文章

  • ThreadPool-线程池使用及原理
    1.线程池使用方式示例代码://一池N线程Executors.newFixedThreadPool(int)//一个任务一个任务执行,一池一线程Executors.newSingleThreadExecutorO//线程池根据需求创建线程,可扩容,遇强则强Executors.newCachedThreadPool()//自定义线程池方式newThreadPoolExec......
  • 【性能测试】线程数、并发数和TPS的关系
    项目背景某通信产品在提测阶段,领导要求支持1w人同时在线,支持1000并发,去测吧理解需求“支持1w人同时在线,支持1000并发”“1w人同时在线”这个理解起来简单一些,对于即时通讯产品来说,就是1w个长链接,直接写脚本建立长链接就行。“支持1000并发”这里就产生歧义了:1、什么功......
  • 线程池
    线程池引入一个线程完成一项任务所需时间为:创建线程时间-Time1线程中执行任务的时间-Time2销毁线程时间-Time3为什么需要线程池线程池技术正是关注如何缩短或调整Time1和Time3的时间,从而提高程序的性能。项目中可以把Time1,T3分别安排在项目的启动和结束的时......
  • 操作系统高级议题:并发控制与进程互斥技术
    ✨✨欢迎大家来访Srlua的博文(づ ̄3 ̄)づ╭❤~✨✨......
  • 创建与启动线程之二(继承Thread类)(实现Runnable接口)
    1.概述java的JVM允许程序运行多个线程.使用java.lang.Thread来表示线程.一个线程都直接或间接的继承于Thread类,即每个线程的对象要么是Thread的实例,要么是其子类的实例.2.Thread类的特性每个线程都是通过某个特定的Thread对象的run方法来完成操作的,run()被称为线程执行体.......
  • java声明不同线程池
    声明一个静态线程池publicstaticfinalExecutorServicePOOL;privatestaticThreadFactorynamedThreadFactory=newThreadFactoryBuilder().setNameFormat("COMMON-POOL-%d").build();static{POOL=newThreadPoolExecutor(20,40,300,......
  • spring+struts 配置和管理线程池
    <!--定义线程池--><beanid="taskExecutor"class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"><propertyname="corePoolSize"value="5"/><propertyname="maxPoolSi......
  • linux 线程的一些简答概念
    基本概念1.临界资源:多线程执行流共享的资源2.临界区:访问临界资源的代码3.原子性:只有完成和未完成两种状态。4.互斥:同一时间只能允许一个线程访问临界资源,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。可以加锁实现。加锁可以保证单个线程对临界......
  • Java中创建线程的多种方式实例
    在Java中创建线程的方式有多种,以下是几种常见的创建线程的方式,选择哪种方式取决于具体的需求和设计。需要注意的是,在使用多线程时,要确保线程安全,避免出现并发问题。1、普通Thread创建//继承创建线程staticclassNewThreadextendsThread{@Override......
  • GCD 并发队列来实现多读单写
     iOS的多读单写指的是多个线程可以同时读取共享的数据,但是只有一个线程能够写入数据。这是为了保证数据的一致性和避免竞争条件的出现。一在Objective-C中,可以使用GCD的并发队列来实现多读单写。具体实现步骤如下:1.定义一个并发队列和一个串行队列,用于处理读操作和写操......