深入浅出:C语言线程以及线程锁

简介: 线程锁的基本思想是,只有一个线程能持有锁,其他试图获取锁的线程将被阻塞,直到锁被释放。这样,锁就确保了在任何时刻,只有一个线程能够访问临界区(即需要保护的代码段或数据),从而保证了数据的完整性和一致性。线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。一个进程可以包含一个或多个线程,而每个线程都有自己的指令指针和寄存器状态,它们共享进程的资源,如内存空间、文件句柄和网络连接等。线程锁的概念

目录

线程和线程锁概念

线程锁的概念

线程的特点

线程的使用

创建线程 pthread_create

回收线程pthread_join

退出线程 pthread_exit

线程锁的使用

线程同步之互斥锁(Mutex)

初始化互斥锁

获取互斥锁

释放互斥锁

销毁互斥锁

初始化条件变量

等待条件变量

发送信号

广播信号

销毁条件变量

实例:每次打印都实现翻转数组


线程和线程锁概念

线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。一个进程可以包含一个或多个线程,而每个线程都有自己的指令指针和寄存器状态,它们共享进程的资源,如内存空间、文件句柄和网络连接等。

线程锁的概念

线程锁的基本思想是,只有一个线程能持有锁,其他试图获取锁的线程将被阻塞,直到锁被释放。这样,锁就确保了在任何时刻,只有一个线程能够访问临界区(即需要保护的代码段或数据),从而保证了数据的完整性和一致性。

线程的特点

  1. 轻量级进程:相比进程,线程的创建和切换成本更低,因为它们共享相同的地址空间和资源,不需要进行系统调用和上下文切换。
  2. 并发执行:线程允许在一个进程内部并发执行多个控制流,使得程序能够同时处理多个任务,提高程序的响应速度和效率。
  3. 资源共享:线程共享进程的资源,如内存、文件句柄和网络连接等,这减少了资源的开销,但也要求对共享资源进行同步和保护,以防止数据竞争和不一致。
  4. 通信便捷:由于线程共享同一进程的资源,它们之间的通信比进程间通信更为简单和快速,通常只需要使用局部变量或全局变量即可。
  5. 独立调度和执行:线程可以独立于其他线程进行调度和执行,操作系统可以根据需要将CPU时间分配给不同的线程,而无需切换到不同的进程。
  6. 线程状态:线程也有自己的生命周期,包括创建、就绪、运行、阻塞和终止等状态。线程状态的变化由操作系统调度器控制。
  7. 线程同步:为了保证数据的一致性和完整性,线程在访问共享资源时需要进行同步。常用的同步机制包括互斥锁(mutex)、信号量(semaphore)、条件变量(condition variable)等。
  8. 线程间通信:虽然线程共享资源,但在某些情况下,线程之间也需要进行通信,如通知某一线程完成特定任务或传递数据。这可以通过共享内存、信号量或条件变量等方式实现。
  9. 线程优先级:线程可以有不同的优先级,高优先级的线程在调度时会得到更多的CPU时间,从而影响线程的执行顺序和进程的整体性能。

线程的使用

创建线程 pthread_create

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, 
                    void *(*start_routine) (void *), void *arg);
功能:创建线程
参数:thread:线程标识
            attr:线程属性, NULL:代表设置默认属性
            start_routine:函数名:代表线程函数
            arg:用来给前面函数传参
返回值:成功:0
              失败:错误码

image.gif

回收线程pthread_join

int  pthread_join(pthread_t thread,  void **value_ptr) 
功能:用于等待一个指定的线程结束,阻塞函数
参数:thread:创建的线程对象
        value_ptr:指针*value_ptr指向线程返回的参数
返回值:成功 : 0
       失败:errno

image.gif

退出线程 pthread_exit

int  pthread_exit(void *value_ptr) 
功能:用于退出线程的执行
参数:value_ptr:线程退出时返回的值(任意类型)
返回值:成功 : 0
        失败:errno

image.gif

简单用线程实现一下主线程循环从终端输入,线程函数将数据循环输出,当输入quit结束程序。

image.gif 编辑

线程锁的使用

线程同步之互斥锁(Mutex)

互斥锁(Mutex)是一种用于同步线程访问共享资源的机制,确保在任何时刻只有一个线程能够访问临界区,从而避免了数据竞争和不一致性问题。以下是互斥锁相关函数的详细解析:

初始化互斥锁

C

1int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);

image.gif

  • 功能:初始化互斥锁。
  • 参数
  • mutex:指向要初始化的互斥锁的指针。
  • attr:指向互斥锁属性结构的指针,如果为NULL,则使用默认属性。
  • 返回值:成功返回0,失败返回非零值。

获取互斥锁

C

1int pthread_mutex_lock(pthread_mutex_t *mutex);

image.gif

  • 功能:尝试获取互斥锁,如果锁已经被另一个线程持有,当前线程将被阻塞,直到锁可用。
  • 参数
  • mutex:指向要获取的互斥锁的指针。
  • 返回值:成功返回0,失败返回非零值。

pthread_mutex_lockpthread_mutex_trylock的区别在于,pthread_mutex_lock是阻塞的,即如果锁被占用,调用线程会等待直至获取到锁;而pthread_mutex_trylock是非阻塞的,如果锁被占用,它会立即返回,不等待锁释放。

释放互斥锁

C

1int pthread_mutex_unlock(pthread_mutex_t *mutex);

image.gif

  • 功能:释放由当前线程持有的互斥锁。
  • 参数
  • mutex:指向要释放的互斥锁的指针。
  • 返回值:成功返回0,失败返回非零值。

销毁互斥锁

C

1int pthread_mutex_destroy(pthread_mutex_t *mutex);

image.gif

  • 功能:销毁互斥锁,通常在不再需要互斥锁时调用。
  • 参数
  • mutex:指向要销毁的互斥锁的指针。
  • 返回值:成功返回0,失败返回非零值。

初始化条件变量

C

1int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);

image.gif

  • 功能:初始化条件变量。
  • 参数
  • cond:指向要初始化的条件变量的指针。
  • attr:指向条件变量属性的指针,通常设为NULL以使用默认属性。
  • 返回值:成功返回0,失败返回非零值。

等待条件变量

C

1int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);

image.gif

  • 功能:线程挂起等待,直到条件变量被信号唤醒。
  • 参数
  • cond:指向要等待的条件变量的指针。
  • mutex:与条件变量关联的互斥锁,必须在调用前锁定并在返回后再次锁定。
  • 返回值:成功返回0,失败返回非零值。
  • 注释:当没有条件产生时函数会阻塞,同时会自动解锁互斥锁;一旦条件产生或接收到信号,函数结束阻塞并重新锁定互斥锁。

发送信号

C

1int pthread_cond_signal(pthread_cond_t *cond);

image.gif

  • 功能:向条件变量发送信号,唤醒一个等待该条件变量的线程。
  • 参数
  • cond:指向条件变量的指针。
  • 返回值:成功返回0,失败返回非零值。
  • 注释:通常在条件满足后调用,以通知等待线程继续执行。

广播信号

C

1int pthread_cond_broadcast(pthread_cond_t *cond);

image.gif

  • 功能:向条件变量广播信号,唤醒所有等待该条件变量的线程。
  • 参数
  • cond:指向条件变量的指针。
  • 返回值:成功返回0,失败返回非零值。
  • 注释:与pthread_cond_signal类似,但唤醒所有等待线程,而非仅唤醒一个。

销毁条件变量

C

1int pthread_cond_destroy(pthread_cond_t *cond);

image.gif

  • 功能:销毁条件变量。
  • 参数
  • cond:指向要销毁的条件变量的指针。
  • 返回值:成功返回0,失败返回非零值。

实例:每次打印都实现翻转数组

. image.gif 编辑

image.gif 编辑

注:由于线程是随机执行的,所以此时我们不得不用上互斥锁,否则就会出现一些意外情况,这与我们使用标志位是一个道理,例如flag为true的时候p1执行,为flase的p2执行,但是线程锁的逻辑更加严谨和细致一些,功能也更多一些。

相关文章
|
24天前
|
安全 Java 编译器
线程安全问题和锁
本文详细介绍了线程的状态及其转换,包括新建、就绪、等待、超时等待、阻塞和终止状态,并通过示例说明了各状态的特点。接着,文章深入探讨了线程安全问题,分析了多线程环境下变量修改引发的数据异常,并通过使用 `synchronized` 关键字和 `volatile` 解决内存可见性问题。最后,文章讲解了锁的概念,包括同步代码块、同步方法以及 `Lock` 接口,并讨论了死锁现象及其产生的原因与解决方案。
56 10
线程安全问题和锁
|
5天前
|
安全 Java 调度
Java编程时多线程操作单核服务器可以不加锁吗?
Java编程时多线程操作单核服务器可以不加锁吗?
18 2
|
19天前
|
存储 缓存 安全
【Java面试题汇总】多线程、JUC、锁篇(2023版)
线程和进程的区别、CAS的ABA问题、AQS、哪些地方使用了CAS、怎么保证线程安全、线程同步方式、synchronized的用法及原理、Lock、volatile、线程的六个状态、ThreadLocal、线程通信方式、创建方式、两种创建线程池的方法、线程池设置合适的线程数、线程安全的集合?ConcurrentHashMap、JUC
【Java面试题汇总】多线程、JUC、锁篇(2023版)
|
2月前
|
数据采集 存储 安全
如何确保Python Queue的线程和进程安全性:使用锁的技巧
本文探讨了在Python爬虫技术中使用锁来保障Queue(队列)的线程和进程安全性。通过分析`queue.Queue`及`multiprocessing.Queue`的基本线程与进程安全特性,文章指出在特定场景下使用锁的重要性。文中还提供了一个综合示例,该示例利用亿牛云爬虫代理服务、多线程技术和锁机制,实现了高效且安全的网页数据采集流程。示例涵盖了代理IP、User-Agent和Cookie的设置,以及如何使用BeautifulSoup解析HTML内容并将其保存为文档。通过这种方式,不仅提高了数据采集效率,还有效避免了并发环境下的数据竞争问题。
如何确保Python Queue的线程和进程安全性:使用锁的技巧
|
1月前
|
网络协议 C语言
C语言 网络编程(十四)并发的TCP服务端-以线程完成功能
这段代码实现了一个基于TCP协议的多线程服务器和客户端程序,服务器端通过为每个客户端创建独立的线程来处理并发请求,解决了粘包问题并支持不定长数据传输。服务器监听在IP地址`172.17.140.183`的`8080`端口上,接收客户端发来的数据,并将接收到的消息添加“-回传”后返回给客户端。客户端则可以循环输入并发送数据,同时接收服务器回传的信息。当输入“exit”时,客户端会结束与服务器的通信并关闭连接。
|
1月前
|
存储 Ubuntu Linux
C语言 多线程编程(1) 初识线程和条件变量
本文档详细介绍了多线程的概念、相关命令及线程的操作方法。首先解释了线程的定义及其与进程的关系,接着对比了线程与进程的区别。随后介绍了如何在 Linux 系统中使用 `pidstat`、`top` 和 `ps` 命令查看线程信息。文档还探讨了多进程和多线程模式各自的优缺点及适用场景,并详细讲解了如何使用 POSIX 线程库创建、退出、等待和取消线程。此外,还介绍了线程分离的概念和方法,并提供了多个示例代码帮助理解。最后,深入探讨了线程间的通讯机制、互斥锁和条件变量的使用,通过具体示例展示了如何实现生产者与消费者的同步模型。
|
1月前
|
C语言
C语言 网络编程(九)并发的UDP服务端 以线程完成功能
这是一个基于UDP协议的客户端和服务端程序,其中服务端采用多线程并发处理客户端请求。客户端通过UDP向服务端发送登录请求,并根据登录结果与服务端的新子线程进行后续交互。服务端在主线程中接收客户端请求并创建新线程处理登录验证及后续通信,子线程创建新的套接字并与客户端进行数据交换。该程序展示了如何利用线程和UDP实现简单的并发服务器架构。
|
8天前
|
存储 算法 Java
关于python3的一些理解(装饰器、垃圾回收、进程线程协程、全局解释器锁等)
该文章深入探讨了Python3中的多个重要概念,包括装饰器的工作原理、垃圾回收机制、进程与线程的区别及全局解释器锁(GIL)的影响等,并提供了详细的解释与示例代码。
15 0
|
2月前
|
Java 开发者
Java多线程教程:使用ReentrantLock实现高级锁功能
Java多线程教程:使用ReentrantLock实现高级锁功能
34 1
|
1月前
|
安全 Java API
Java线程池原理与锁机制分析
综上所述,Java线程池和锁机制是并发编程中极其重要的两个部分。线程池主要用于管理线程的生命周期和执行并发任务,而锁机制则用于保障线程安全和防止数据的并发错误。它们深入地结合在一起,成为Java高效并发编程实践中的关键要素。
15 0
下一篇
无影云桌面