Linux多线程同步机制(下)

简介: Linux多线程同步机制(下)

一、读写锁

线程同步机制中的读写锁(Read-Write Lock)是一种特殊的锁机制,用于控制对共享资源的读写访问。读写锁允许多个线程同时读取共享资源,但在写操作时需要独占访问。

读写锁的基本原则是:多个线程可以同时获取读锁,但只有一个线程可以获取写锁。当有线程持有写锁时,其他线程无法获取读锁或写锁,直到写操作完成并释放写锁。

读写锁有两种状态:读模式下加锁状态(读锁),写模式下加锁状态(写锁)。读写锁只有一把。

1. 读写锁的特性:写独占,读共享。

  • 读写锁是 “ 写模式加锁 ” 时,解锁前,所有对该锁加锁的线程都会被阻塞。
  • 读写锁是 “ 读模式加锁 ” 时,如果线程以读模式 则对其加锁会成功;如果线程是以写模式加锁会阻塞。
  • 读写锁是 “ 读模式加锁 ” 时,既有试图以写模式加锁的线程,也有试图以读模式加锁的线程。那么读写锁会阻塞读模式请求。优先满足写模式加锁。读写锁并行阻塞,写锁优先级高。

2. 读写锁相关函数:

  • 初始化,pthread_rwlock_init
  • 获取读锁:使用pthread_rwlock_rdlock函数获取读锁。
  • 获取写锁:使用pthread_rwlock_wrlock函数获取写锁。
  • 释放锁:使用pthread_rwlock_unlock函数释放读锁或写锁。
  • 销毁读写锁:在不再需要时,使用pthread_rwlock_destroy函数销毁读写锁。

3. 示例代码:

下面代码设置一个写进程 和 三个读进程区访问共享资源。

(void*)i 将整数 i 转换为 void* 类型。在线程的函数中,需要将其转换回整数类型,以便使用它。可以使用 (int)arg 将 void* 转换回 int 类型。void* 是一种通用的泛型指针,可以在不关心具体类型的情况下进行转换和操作。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <time.h>
#include <unistd.h>
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;   // 初始化读写锁
int data = 1;
void *write_thread (void *arg)
{
  while(1)
  {
    pthread_rwlock_wrlock(&rwlock);
    data += 1;
    printf("write : data = %d\n",data); 
    pthread_rwlock_unlock(&rwlock);
    sleep(rand() %3);         // 随机休眠,让出 cpu资源
  }
  pthread_exit(NULL);
}
void *read_thread (void *arg)
{
  int i = (int )arg;
  while(1)
  {
    pthread_rwlock_rdlock(&rwlock);
    printf("read %d : data = %d\n",i, data);  
    pthread_rwlock_unlock(&rwlock);
    sleep(rand() %3);
  }
  pthread_exit(NULL);
}
int main(void)
{
  pthread_t r_tid[5], w_tid;
  srand(time(NULL));
  // 创建多个读线程
      for (int i = 0; i < 3; i++) 
  {
     pthread_create(&r_tid[i], NULL, read_thread, (void*)i);
  }
  pthread_create(&w_tid, NULL, write_thread, NULL);
  for(int j=0;j<3;j++)
    pthread_join(r_tid[j],NULL);
  pthread_join(w_tid,NULL);
  pthread_rwlock_destroy(&rwlock);
  return 0;
}

二、条件变量

在多线程编程中,条件变量是一种用于线程之间进行通信和同步的机制。条件变量允许一个线程等待特定的条件发生,并在条件满足时通知其他线程继续执行。条件变量本身不是锁。条件变量常与互斥锁(mutex)结合使用,以实现线程之间的同步操作。

1. 相关函数:

(1) 对条件变量进行初始化,并可指定属性。通常使用默认属性,可以传入 NULL。

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

(2) 销毁条件变量,释放相关资源。

int pthread_cond_destroy(pthread_cond_t *cond);

当前线程等待条件变量满足,并释放关联的互斥锁。该函数会阻塞线程直至条件变量被通知。

在调用 pthread_cond_wait() 之前,必须先获得与条件变量关联的互斥锁 mutex 的锁,然后该函数会自动释放 mutex 的锁(自动 unlock),并让线程

  • 进入等待状态,直到被另一个线程通过 pthread_cond_signal() 或 pthread_cond_broadcast() 唤醒(自动 lock)。
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

(3) 通知等待在条件变量上的一个线程,使其继续执行。如果有多个线程等待,只通知其中一个线程。

int pthread_cond_signal(pthread_cond_t *cond);

(4) 通知所有等待在条件变量上的线程,使它们都继续执行。

int pthread_cond_broadcast(pthread_cond_t *cond) ;

2. 示例代码:

描述生产者 和 消费者的关系。

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
struct msg
{
  int num;
  struct msg* pnext;
};
struct msg *head = NULL;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
void *producer_thread (void *arg)             // 生产者线程
{
  while(1)
  { 
    struct msg *info = malloc(sizeof(struct msg));
    info->num = rand() % 1000 + 1;
    pthread_mutex_lock(&mutex);
    info->pnext = head;
    head = info;
    pthread_mutex_unlock(&mutex);
    // 唤醒阻塞的线程
    pthread_cond_signal(&cond);     
    printf("--------------------- producer : %d\n",info->num);
    sleep(rand() % 3);
  }
  return NULL;
}
void *customer_thread (void *arg)         // 消费者线程
{
  while(1)
  {
    struct msg *info;   
    pthread_mutex_lock(&mutex);
    if(head == NULL)
    {
      pthread_cond_wait(&cond, &mutex);     // 阻塞线程,等待有数据。
    }
    info = head;
    head = info->pnext;
    pthread_mutex_unlock(&mutex);
    printf("customer : =============== %d\n",info->num);
    free(info);
    info = NULL;
    sleep(rand() % 3);
  }
  return NULL;
}
int main(void)
{
  pthread_t pid,cid;
  srand(time(NULL));            // 设置随机种子
  int ret = pthread_create(&pid, NULL, producer_thread, NULL);
  if(ret != 0)
  {
    printf("prodecer_thread_create error\n");
  }
  ret = pthread_create(&cid, NULL, customer_thread, NULL);
  if(ret != 0)
  {
    printf("consumer_thread_create error\n");
  }
  pthread_join(pid, NULL);          // 等待回收线程,获取退出线程的状态
  pthread_join(cid, NULL);
  pthread_mutex_destroy(&mutex);
  pthread_cond_destroy(&cond);
  return 0;
}

总结

相关文章
|
6月前
|
安全 算法 Java
Java 多线程:线程安全与同步控制的深度解析
本文介绍了 Java 多线程开发的关键技术,涵盖线程的创建与启动、线程安全问题及其解决方案,包括 synchronized 关键字、原子类和线程间通信机制。通过示例代码讲解了多线程编程中的常见问题与优化方法,帮助开发者提升程序性能与稳定性。
274 0
|
11月前
|
存储 Linux API
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
在计算机系统的底层架构中,操作系统肩负着资源管理与任务调度的重任。当我们启动各类应用程序时,其背后复杂的运作机制便悄然展开。程序,作为静态的指令集合,如何在系统中实现动态执行?本文带你一探究竟!
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
|
9月前
|
并行计算 Linux
Linux内核中的线程和进程实现详解
了解进程和线程如何工作,可以帮助我们更好地编写程序,充分利用多核CPU,实现并行计算,提高系统的响应速度和计算效能。记住,适当平衡进程和线程的使用,既要拥有独立空间的'兄弟',也需要在'家庭'中分享和并行的成员。对于这个世界,现在,你应该有一个全新的认识。
314 67
|
存储 编译器 Linux
动态链接的魔法:Linux下动态链接库机制探讨
本文将深入探讨Linux系统中的动态链接库机制,这其中包括但不限于全局符号介入、延迟绑定以及地址无关代码等内容。
2153 141
|
9月前
|
存储 Linux
Linux内核中的current机制解析
总的来说,current机制是Linux内核中进程管理的基础,它通过获取当前进程的task_struct结构的地址,可以方便地获取和修改进程的信息。这个机制在内核中的使用非常广泛,对于理解Linux内核的工作原理有着重要的意义。
412 11
|
10月前
|
Ubuntu Linux
Linux系统管理:服务器时间与网络时间同步技巧。
以上就是在Linux服务器上设置时间同步的方式。然而,要正确运用这些知识,需要理解其背后的工作原理:服务器根据网络中的其他机器的时间进行校对,逐步地精确自己的系统时间,就像一只犹豫不决的啮齿动物,通过观察其他啮齿动物的行为,逐渐确定自己的行为逻辑,既简单,又有趣。最后希望这个过程既能给你带来乐趣,也能提高你作为系统管理员的专业素养。
1425 20
|
12月前
|
并行计算 安全 Java
Python GIL(全局解释器锁)机制对多线程性能影响的深度分析
在Python开发中,GIL(全局解释器锁)一直备受关注。本文基于CPython解释器,探讨GIL的技术本质及其对程序性能的影响。GIL确保同一时刻只有一个线程执行代码,以保护内存管理的安全性,但也限制了多线程并行计算的效率。文章分析了GIL的必要性、局限性,并介绍了多进程、异步编程等替代方案。尽管Python 3.13计划移除GIL,但该特性至少要到2028年才会默认禁用,因此理解GIL仍至关重要。
925 16
Python GIL(全局解释器锁)机制对多线程性能影响的深度分析
|
11月前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
通过本文,您可以了解如何在业务线程中注册和处理Linux信号。正确处理信号可以提高程序的健壮性和稳定性。希望这些内容能帮助您更好地理解和应用Linux信号处理机制。
216 26
|
11月前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
本文详细介绍了如何在Linux中通过在业务线程中注册和处理信号。我们讨论了信号的基本概念,并通过完整的代码示例展示了在业务线程中注册和处理信号的方法。通过正确地使用信号处理机制,可以提高程序的健壮性和响应能力。希望本文能帮助您更好地理解和应用Linux信号处理,提高开发效率和代码质量。
218 17
|
监控 算法 Linux
Linux内核锁机制深度剖析与实践优化####
本文作为一篇技术性文章,深入探讨了Linux操作系统内核中锁机制的工作原理、类型及其在并发控制中的应用,旨在为开发者提供关于如何有效利用这些工具来提升系统性能和稳定性的见解。不同于常规摘要的概述性质,本文将直接通过具体案例分析,展示在不同场景下选择合适的锁策略对于解决竞争条件、死锁问题的重要性,以及如何根据实际需求调整锁的粒度以达到最佳效果,为读者呈现一份实用性强的实践指南。 ####