欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 高考 > C++学习之线程同步

C++学习之线程同步

2025/11/17 19:17:20 来源:https://blog.csdn.net/qq_27302885/article/details/147011746  浏览:    关键词:C++学习之线程同步

目录

1.线程同步相关概念

2.锁属性-建议锁

3.Mutex互斥锁操作

4.互斥锁使用注意事项

5.互斥量的初始化方法

6.死锁

7.读写锁特性

8.读写锁操作函数

9.读写锁使用示例

10.条件变量操作函数

11.生产者消费者模型简单分析

12.条件变量实现生产者消费者模型代码预览

13.条件变量实现生产者消费者模型流程分析

14.条件变量实现生产者消费模型

15.wait放置到while循环中

16.信号量和信号量操作函数

17.信号量生产者消费者图示分析

18.信号量生产者消费者代码示例


1.线程同步相关概念

 同步:即协同步调,按预定的先后次序访问共享数据。
- 线程同步,指一个线程发出某一功能调用时,没有得到结果之前,该调用不返回。同时,其他线程为保证数据一致性,不调用该功能。

2.锁属性-建议锁

 数据混乱产生的原因
    1. 资源共享
    2. 调度随机
    3. 线程之间缺乏必要同步机制。
- 只能从第3步着手解决 “数据混乱”, 避免产生与时间有关的错误。执行多个控制流(线程)访问共享的数据的先后顺序。

- 锁的属性:
    - 系统提供内用户使用的所有的锁,全部为 “建议锁”, 不具备强制性。
    - 如果访问共享数据的线程,不拿锁,直接访问共享数据,能访问成功。但是,数据就有出现混乱的风险。  —— 拿锁与否完全取决于编写的程序。

3.Mutex互斥锁操作

```c
pthread_mutex_t mutex;  创建互斥锁

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);    初始化互斥锁

int pthread_mutex_lock(pthread_mutex_t *mutex); 加锁, 如果锁被占用,阻塞等。

int pthread_mutex_trylock(pthread_mutex_t *mutex); 尝试加锁,不阻塞。

int pthread_mutex_unlock(pthread_mutex_t *mutex);  解锁, 唤醒阻塞在此把锁上的线程。

int pthread_mutex_destroy(pthread_mutex_t *mutex); 销毁互斥锁
```

4.互斥锁使用注意事项

- 注意事项:
    1.  保证锁的粒度 , 越小越好!( 访问共享数据前,加锁,访问共享数据结束,立即解锁。)
    2.  互斥锁,本质:结构体。 我们可以把它当成整数看待。 初值为 1(init成功)。 取值:1 或 0 
    3.  加锁:--操作。1--  ---> 0  。 失败:阻塞线程。
    4.  解锁:++操作。0++ ---> 1。 成功:唤醒阻塞在锁上的线程。
    5.  try锁:尝试加锁。 成功--。 失败,设置错误号 EBUSY、EINVAL

5.互斥量的初始化方法

- 初始化互斥量

    ```c
    pthread_mutex_t mutex;
    
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;// 静态初始化。--- 常用于全局变量
    pthread_mutex_init(&mutex, NULL);    // 动态初始化。--- 常用于函数内部初始化。
    ```

6.死锁

- 死锁不是一种锁!是错误使用锁的一种状态。
- 常见死锁种类:
    1.  对一把锁,反复lock。
    2.  两个线程, 各自持有一把锁,请求另一把锁。

7.读写锁特性

### 3句话描述

1. 锁,只有一把!可以指定为 “读模式” 和 “写模式”
2. 读共享,写独占。
3. 写锁优先级高!( 读锁已经加锁成功,写锁无法加锁成功,阻塞等。)

8.读写锁操作函数

```c
pthread_rwlock_t  rwlock; // 创建读写锁对象。

pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;  // 静态初始化读写锁。
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
            const pthread_rwlockattr_t *restrict attr); // 动态初始化读写锁。

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);  // 读模式加锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);  // 写模式加锁

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);  // 解锁
```

9.读写锁使用示例

 restrict 关键字:
    - 用来修饰指针变量。被该关键字修饰的指针变量所指向的内存操作,只能由本指针完成。

10.条件变量操作函数

 条件变量不是锁! 结合 互斥锁 使用。 也能造成线程阻塞。

11.生产者消费者模型简单分析

12.条件变量实现生产者消费者模型代码预览

13.条件变量实现生产者消费者模型流程分析

```c
void err_thread(int ret, char *str)
{
    if (ret != 0) {
        fprintf(stderr, "%s:%s\n", str, strerror(ret));
        pthread_exit(NULL);
    }
}
// 创建公共区
struct msg {
    int num;
    struct msg *next;
};
struct msg *head = NULL;

14.条件变量实现生产者消费模型

void *producer(void *arg)
{
    while (1) {
        struct msg *p = malloc(sizeof(struct msg));
        // 生产数据
        p->num = rand() % 1000 + 1;    
        printf("----produce:%d\n", p->num);

        // 将数据保存至公共区
        pthread_mutex_lock(&mutex);
        p->next = head;
        head = p;                        // 头插法,写入数据
        pthread_mutex_unlock(&mutex);

        // 通知消费者
        pthread_cond_signal(&has_data);

        sleep(rand() % 3);
    }

    return NULL;
}

15.wait放置到while循环中

int main(int argc, char *argv[])
{
    int ret;
    pthread_t pid, cid;

    srand(time(NULL));        // 播种随机数种子

    // 生产者
    ret = pthread_create(&pid, NULL, producer, NULL);
    if (ret != 0)
        err_thread(ret, "pthread_create producer:");

    // 消费者
    ret = pthread_create(&cid, NULL, consumer, NULL);
    if (ret != 0)
        err_thread(ret, "pthread_create consumer:");

    ret = pthread_create(&cid, NULL, consumer, NULL);
    if (ret != 0)
        err_thread(ret, "pthread_create consumer:");

    ret = pthread_create(&cid, NULL, consumer, NULL);
    if (ret != 0)
        err_thread(ret, "pthread_create consumer:");


    pthread_join(pid, NULL);
    pthread_join(cid, NULL);

    return 0;
}

16.信号量和信号量操作函数

- 信号量与信号无关!

- 信号量 相当于 初始值为 N 的互斥量。 N值,表示可以同时访问共享数据的线程数。

17.信号量生产者消费者图示分析

```c
#include <semaphore.h>
sem_t sem; // 创建信号量对象
int sem_init(sem_t *sem, int pshared, unsigned int value);    //动态初始化方法。没有静态
    参2 pshared:     0: 用于线程间同步
                 非0(1): 用于进程间同步
    参3 value:信号量的初值。 N值。代替同时访问共享数据的线程个数。                         返回值:
    成功:0
    失败:-1, errrno --- perror()
        
int sem_destroy(sem_t *sem);

int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
           struct timespec {
               time_t tv_sec;      /* Seconds */
               long   tv_nsec;     /* 纳秒 Nanoseconds [0 .. 999999999] */
           };
    abs:绝对。
    绝对时间:从 1970年1月1日 00:00:00 开始,计算时间。
    
int sem_wait(sem_t *sem);
    一次调用,做一次--操作。当信号量的值为0时, 再次--,会阻塞。(对比pthread_mutex_lock)
    

18.信号量生产者消费者代码示例

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词