【C语言】线程同步

简介: 【C语言】线程同步

线程同步

  线程同步是指在多线程的情况下,如果多个线程去访问共享资源,需要按照一定规则顺序依次去访问,保证共享资源的数据一致性。

1. 互斥锁

互斥相关函数

//互斥量
pthread_mutex_t mutex;

//pthread_mutex_init()
//互斥量初始化
int pthread_mutex_init(pthread_mutex_t *restrict mutex, 
                                const pthread_mutexattr_t *restrict attr);
参数
    mutex 传出参数
    attr 互斥量属性,通常设为NULL,线程间共享 

//pthread_mutex_lock()
//互斥量加锁
int pthread_mutex_lock(pthread_mutex_t *mutex);

//pthread_mutex_unlock()
//互斥量解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);

//pthread_mutex_trylock
//尝试加锁
int pthread_mutex_trylock(pthread_mutex_t *mutex);

//pthread_mutex_destroy()
//销毁互斥量
int pthread_mutex_destroy(pthread_mutex_t *mutex);

示例:

多线程访问全局变量num,并实现同步。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>

int num = 0;
pthread_mutex_t mutex;

void *threadMethod1(void *arg)
{
   
    int i, cur;
    while (num<5000)
    {
   
        pthread_mutex_lock(&mutex);
        if (num == 5000)
        {
   
            break;
        }
        cur = num;
        cur++;
        num = cur;

        printf("[%ld] num [%d]\n", pthread_self(), num);
        pthread_mutex_unlock(&mutex);
    }
}

void *threadMethod2(void *arg)
{
   
    int i, cur;

    while (num<5000)
    {
   
        pthread_mutex_lock(&mutex);
        if (num == 5000)
        {
   
            break;
        }

        cur = num;
        cur++;
        num = cur;

        printf("[%ld] num [%d]\n", pthread_self(), num);
        pthread_mutex_unlock(&mutex);
    }

    // for (i = 0; i < 5000; i++)
    // {
   
    //     pthread_mutex_lock(&mutex);
    //     cur = num;
    //     cur++;
    //     num = cur;

    //     printf("[%ld] num [%d]\n", pthread_self(), num);
    //     pthread_mutex_unlock(&mutex);
    // }
}

int main()
{
   

    pthread_mutex_init(&mutex, NULL);
    pthread_t thread1, thread2;

    int ret = pthread_create(&thread1, NULL, threadMethod1, NULL);
    if (ret != 0)
    {
   
        printf("pthread_create error, [%s]\n", strerror(ret));
        return -1;
    }
    ret = pthread_create(&thread2, NULL, threadMethod1, NULL);
    if (ret != 0)
    {
   
        printf("pthread_create error, [%s]\n", strerror(ret));
        return -1;
    }

    void *exit_status;
    ret = pthread_join(thread1, &exit_status);
    if (ret)
    {
   
        printf("Thread join error: %d\n", ret);
        return -1;
    }
    ret = pthread_join(thread2, &exit_status);
    if (ret)
    {
   
        printf("Thread join error: %d\n", ret);
        return -1;
    }

    printf("main thread, pid is [%d], tid is [%ld]\n", getpid(), pthread_self());
    pthread_mutex_destroy(&mutex);

    return 0;
}

2. 读写锁

读共享,写独占,适用于读多余写的情况。读写同时等待时,写的优先级更高。

读写锁相关函数:

//1. 读写锁变量
pthread_rwlock_t rwlock;
//2. 读写锁初始化,参数attr为读写锁属性,设置为NULL表示设置为默认属性
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
//3. 加读锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
//4.尝试加读锁
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
//5. 加写锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
//6. 尝试加写锁
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
//7.解锁
int pthread_rwlock_unlock(&pthread_rwlock_t *rwlock);
//8. 销毁锁
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

示例:

5个读线程,3个写线程来访问共享资源num

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>

#define FINALNUM 100
int num = 0;
pthread_rwlock_t rdlock;

// 读线程
void *threadMethod1(void *arg)
{
   
    int cur;
    while (num<FINALNUM)
    {
   
        pthread_rwlock_rdlock(&rdlock);
        if (num > FINALNUM)
        {
   
            pthread_rwlock_unlock(&rdlock);
            break;
        }

        cur = num;

        printf("read [%d] : [%ld] num [%d]\n",*(int *)arg, pthread_self(), cur);
        pthread_rwlock_unlock(&rdlock);  
        sleep(rand()%3);
    }
}
// 写线程
void *threadMethod2(void *arg)
{
   
    int cur;

    while (num < FINALNUM)
    {
   
        pthread_rwlock_wrlock(&rdlock);
        if (num == FINALNUM)
        {
   
            pthread_rwlock_unlock(&rdlock);
            break;
        }

        cur = num;
        cur++;
        num = cur;

        printf("write [%d] : [%ld] num [%d]\n",*(int *)arg, pthread_self(), cur);
        pthread_rwlock_unlock(&rdlock);
        sleep(rand()%3);
    }
}

int main()
{
   

    pthread_rwlock_init(&rdlock, NULL);
    pthread_t threadr[5], threadw[3];
    int i = 0;
    int ret;
    int arr[8];
    // 5个读线程
    for (i = 0; i < 5; i++)
    {
   
        arr[i] = i + 1;
        ret = pthread_create(&threadr[i], NULL, threadMethod1, &arr[i]);
        if (ret != 0)
        {
   
            printf("pthread_create read [%d] error, [%s]\n", i+1, strerror(ret));
            return -1;
        }
    }
    // 3个写线程
    for (i = 0; i < 3; i++)
    {
   
        arr[i + 5] = i + 1;
        ret = pthread_create(&threadw[i], NULL, threadMethod2, &arr[i + 5]);
        if (ret != 0)
        {
   
            printf("pthread_create write [%d] error, [%s]\n", i+1, strerror(ret));
            return -1;
        }
    }

    void *exit_status;

    for (i = 0; i < 5; i++)
    {
   
        ret = pthread_join(threadr[i], &exit_status);
        if (ret)
        {
   
            printf("Thread join read [%d] error: [%s]\n", i+1, strerror(ret));
            return -1;
        }
    }

    for (i = 0; i < 3; i++)
    {
   
        ret = pthread_join(threadw[i], &exit_status);
        if (ret)
        {
   
            printf("Thread join write [%d] error: [%s]\n", i+1, strerror(ret));
            return -1;
        }
    }

    printf("main thread, pid is [%d], tid is [%ld]\n", getpid(), pthread_self());
    pthread_rwlock_destroy(&rdlock);
    printf("here is main ,num final is [%d]\n",num);

    return 0;
}
//输出
...
read [5] : [140482278684416] num [96]
read [5] : [140482278684416] num [96]
read [3] : [140482295469824] num [96]
write [3] : [140482253506304] num [97]
write [3] : [140482253506304] num [98]
write [3] : [140482253506304] num [99]
read [1] : [140482312255232] num [99]
read [2] : [140482303862528] num [99]
read [4] : [140482287077120] num [99]
write [2] : [140482261899008] num [100]
main thread, pid is [3151], tid is [140482320643840]
here is main ,num final is [100]

3. 条件变量

条件变量不是锁,但是不满足,线程阻塞,释放互斥锁。条件满足,通知所有阻塞的线程去争夺互斥锁。

条件变量相关函数

//1.定义一个条件变量
pthread_cond_t  cond;
//2.条件变量初始化,成功返回0, 失败返回错误编号
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
参数: 
    cond: 条件变量
    attr: 条件变量属性,NULL表示默认属性

//2.条件不满足, 引起线程阻塞并解锁;条件满足, 解除线程阻塞, 并加锁。成功返回0, 失败返回错误号
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);     
参数:
    cond: 条件变量
    mutex: 互斥锁变量
//3.唤醒至少一个阻塞在该条件变量上的线程,成功返回0, 失败返回错误号。
int pthread_cond_signal(pthread_cond_t *cond);

//4.销毁条件变量,成功返回0, 失败返回错误编号
int pthread_cond_destroy(pthread_cond_t *cond);

示例:

多线程模拟生产者消费者模型,并且设置条件变量进行线程同步

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>

typedef struct linklist
{
   
    int num;
    struct linklist *next;
} LINKLIST;

LINKLIST *head = NULL;
pthread_mutex_t mutex;
pthread_cond_t cond;

int len;

void *producter(void *arg)
{
   
    LINKLIST *node = NULL;
    while (1)
    {
   

        pthread_mutex_lock(&mutex);
        node = (LINKLIST *)malloc(sizeof(LINKLIST));
        if (node == NULL)
        {
   
            perror("malloc error");
            exit(-1);
        }
        head->num = head->num + 1;
        len += 1;
        node->num = len;
        node->next = head->next;
        head->next = node;
        printf("linklist len is [%d]\n", len);

        printf("producer [%d] , cur monunt is [%d],get data num [%d]\n", *(int *)arg, head->num, node->num);
        pthread_mutex_unlock(&mutex);
        pthread_cond_signal(&cond);
        sleep(rand() % 3);
    }
}

void *consumer(void *arg)
{
   
    LINKLIST *temp = NULL;
    while (1)
    {
   

        pthread_mutex_lock(&mutex);
        printf("consumer [%d] mutex lock\n", *(int *)arg);
        if (head->next == NULL)
        {
   
            pthread_cond_wait(&cond, &mutex);
            printf("first consumer [%d] cond wait\n", *(int *)arg);
            if (head->next == NULL)
            {
   
                printf("linklist len is [%d]\n", len);
                pthread_mutex_unlock(&mutex);
                printf("first consumer [%d] mutex unlock\n", *(int *)arg);
                continue;
            }
        }
        temp = head->next;

        printf("consumer [%d] , cur monunt is [%d],get data num [%d]\n", *(int *)arg, head->num, temp->num);

        head->next = temp->next;
        temp->next =NULL;
        free(temp);
        temp = NULL;
        head->num = head->num - 1;
        len -= 1;
        pthread_mutex_unlock(&mutex);
        printf("consumer [%d] mutex unlock\n", *(int *)arg);

        sleep(rand() % 3);
    }
}

int main()
{
   
    head = (LINKLIST *)malloc(sizeof(LINKLIST));
    head->num = 0;
    head->next = NULL;
    len = 0;
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);
    pthread_t products[3], consumers[3];
    int arr[6];
    int ret;
    int i;
    for (i = 0; i < 3; i++)
    {
   
        arr[i] = i + 1;
        ret = pthread_create(&products[i], NULL, producter, &arr[i]);
        if (ret != 0)
        {
   
            printf("pthread_create products [%d] error, [%s]\n", i + 1, strerror(ret));
            return -1;
        }
    }

    for (i = 0; i < 3; i++)
    {
   
        arr[i + 3] = i + 1;
        ret = pthread_create(&consumers[i], NULL, consumer, &arr[i + 3]);
        if (ret != 0)
        {
   
            printf("pthread_create consumers [%d] error, [%s]\n", i + 1, strerror(ret));
            return -1;
        }
    }

    void *exit_status;
    for (i = 0; i < 3; i++)
    {
   
        ret = pthread_join(products[i], &exit_status);
        if (ret)
        {
   
            printf("Thread join products [%d] error: %d\n", i + 1, ret);
            return -1;
        }
    }

    for (i = 0; i < 3; i++)
    {
   
        ret = pthread_join(consumers[i], &exit_status);
        if (ret)
        {
   
            printf("Thread join consumers [%d] error: %d\n", i + 1, ret);
            return -1;
        }
    }

    printf("main thread, pid is [%d], tid is [%ld]\n", getpid(), pthread_self());
    pthread_cond_destroy(&cond);
    pthread_mutex_destroy(&mutex);

    return 0;
}

4. 信号量

信号量可以看成是当某个互斥锁有多把的时候,多个线程可以同时访问一个共享资源,当锁不够的时候,就会阻塞。如果锁只有一把,就等同于一个互斥量的情况。

#include <semaphore.h>
//1. 定义信号量 
sem_t sem;
//2. 初始化信号量,成功返回0, 失败返回-1, 并设置errno值
int sem_init(sem_t *sem, int pshared, unsigned int value);    
参数:
    sem: 信号量变量
    pshared: 0表示线程同步, 1表示进程同步
    value: 最多有几个线程操作共享数据

//2. 调用该函数一次, 相当于sem--, 当sem为0的时候, 引起阻塞
//成功返回0, 失败返回-1, 并设置errno值
int sem_wait(sem_t *sem);

//3. 调用一次, 相当于sem++,成功返回0, 失败返回-1, 并设置errno值
int sem_post(sem_t *sem);

//4. 尝试加锁, 若失败直接返回, 不阻塞
//成功返回0, 失败返回-1, 并设置errno值
int sem_trywait(sem_t *sem);

//5. 销毁信号量,成功返回0, 失败返回-1, 并设置errno值
int sem_destroy(sem_t *sem);

示例:

信号量只有一个的时候等同于互斥锁

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <semaphore.h>

typedef struct linklist
{
   
    int num;
    struct linklist *next;
} LINKLIST;

LINKLIST *head = NULL;

// 定义信号量
sem_t sem_producer;
sem_t sem_consumer;

int len;
int tim = 0;

void *producter(void *arg)
{
   
    LINKLIST *node = NULL;
    while (1)
    {
   

        sem_wait(&sem_producer);
        node = (LINKLIST *)malloc(sizeof(LINKLIST));
        if (node == NULL)
        {
   
            perror("malloc error");
            exit(-1);
        }
        head->num = head->num + 1;
        len += 1;
        node->num = len;
        node->next = head->next;
        head->next = node;
        tim++;

        printf("[%d] producer [%d] , cur monunt is [%d],set data num [%d]\n", tim, *(int *)arg, head->num, node->num);
        printf("[%d] linklist len is [%d]\n", tim, len);
        sem_post(&sem_consumer);
        sleep(rand() % 3);
    }
}

void *consumer(void *arg)
{
   
    LINKLIST *temp = NULL;
    while (1)
    {
   

        sem_wait(&sem_consumer);
        tim++;
        temp = head->next;

        printf("[%d] consumer [%d] , cur monunt is [%d],get data num [%d]\n", tim, *(int *)arg, head->num, temp->num);

        head->next = temp->next;
        temp->next = NULL;
        free(temp);
        temp = NULL;
        head->num = head->num - 1;
        len -= 1;
        sem_post(&sem_producer);

        sleep(rand() % 3);
    }
}

int main()
{
   
    head = (LINKLIST *)malloc(sizeof(LINKLIST));
    head->num = 0;
    head->next = NULL;
    len = 0;
    sem_init(&sem_consumer, 0, 0);
    sem_init(&sem_producer, 0, 1);

    pthread_t products[3], consumers[3];
    int arr[6];
    int ret;
    int i;
    for (i = 0; i < 3; i++)
    {
   
        arr[i] = i + 1;
        ret = pthread_create(&products[i], NULL, producter, &arr[i]);
        if (ret != 0)
        {
   
            printf("pthread_create products [%d] error, [%s]\n", i + 1, strerror(ret));
            return -1;
        }
    }

    for (i = 0; i < 3; i++)
    {
   
        arr[i + 3] = i + 1;
        ret = pthread_create(&consumers[i], NULL, consumer, &arr[i + 3]);
        if (ret != 0)
        {
   
            printf("pthread_create consumers [%d] error, [%s]\n", i + 1, strerror(ret));
            return -1;
        }
    }

    void *exit_status;
    for (i = 0; i < 3; i++)
    {
   
        ret = pthread_join(products[i], &exit_status);
        if (ret)
        {
   
            printf("Thread join products [%d] error: %d\n", i + 1, ret);
            return -1;
        }
    }

    for (i = 0; i < 3; i++)
    {
   
        ret = pthread_join(consumers[i], &exit_status);
        if (ret)
        {
   
            printf("Thread join consumers [%d] error: %d\n", i + 1, ret);
            return -1;
        }
    }

    printf("main thread, pid is [%d], tid is [%ld]\n", getpid(), pthread_self());
    sem_destroy(&sem_consumer);
    sem_destroy(&sem_producer);

    return 0;
}
目录
相关文章
|
1月前
|
安全 Java C语言
C语言线程解池解读和实现01
C语言线程解池解读和实现01
|
13天前
|
网络协议 C语言
C语言 网络编程(十四)并发的TCP服务端-以线程完成功能
这段代码实现了一个基于TCP协议的多线程服务器和客户端程序,服务器端通过为每个客户端创建独立的线程来处理并发请求,解决了粘包问题并支持不定长数据传输。服务器监听在IP地址`172.17.140.183`的`8080`端口上,接收客户端发来的数据,并将接收到的消息添加“-回传”后返回给客户端。客户端则可以循环输入并发送数据,同时接收服务器回传的信息。当输入“exit”时,客户端会结束与服务器的通信并关闭连接。
|
13天前
|
存储 Ubuntu Linux
C语言 多线程编程(1) 初识线程和条件变量
本文档详细介绍了多线程的概念、相关命令及线程的操作方法。首先解释了线程的定义及其与进程的关系,接着对比了线程与进程的区别。随后介绍了如何在 Linux 系统中使用 `pidstat`、`top` 和 `ps` 命令查看线程信息。文档还探讨了多进程和多线程模式各自的优缺点及适用场景,并详细讲解了如何使用 POSIX 线程库创建、退出、等待和取消线程。此外,还介绍了线程分离的概念和方法,并提供了多个示例代码帮助理解。最后,深入探讨了线程间的通讯机制、互斥锁和条件变量的使用,通过具体示例展示了如何实现生产者与消费者的同步模型。
|
13天前
|
C语言
C语言 网络编程(九)并发的UDP服务端 以线程完成功能
这是一个基于UDP协议的客户端和服务端程序,其中服务端采用多线程并发处理客户端请求。客户端通过UDP向服务端发送登录请求,并根据登录结果与服务端的新子线程进行后续交互。服务端在主线程中接收客户端请求并创建新线程处理登录验证及后续通信,子线程创建新的套接字并与客户端进行数据交换。该程序展示了如何利用线程和UDP实现简单的并发服务器架构。
|
29天前
|
C语言
【C语言】多线程服务器
【C语言】多线程服务器
15 0
|
29天前
|
程序员 C语言
【C语言】多线程
【C语言】多线程
16 0
|
2月前
|
调度 C语言
深入浅出:C语言线程以及线程锁
线程锁的基本思想是,只有一个线程能持有锁,其他试图获取锁的线程将被阻塞,直到锁被释放。这样,锁就确保了在任何时刻,只有一个线程能够访问临界区(即需要保护的代码段或数据),从而保证了数据的完整性和一致性。 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。一个进程可以包含一个或多个线程,而每个线程都有自己的指令指针和寄存器状态,它们共享进程的资源,如内存空间、文件句柄和网络连接等。 线程锁的概念
|
3月前
|
存储 Linux C语言
c++进阶篇——初窥多线程(二) 基于C语言实现的多线程编写
本文介绍了C++中使用C语言的pthread库实现多线程编程。`pthread_create`用于创建新线程,`pthread_self`返回当前线程ID。示例展示了如何创建线程并打印线程ID,强调了线程同步的重要性,如使用`sleep`防止主线程提前结束导致子线程未执行完。`pthread_exit`用于线程退出,`pthread_join`用来等待并回收子线程,`pthread_detach`则分离线程。文中还提到了线程取消功能,通过`pthread_cancel`实现。这些基本操作是理解和使用C/C++多线程的关键。
|
4月前
|
算法 安全 C语言
【C 言专栏】C 语言中的多线程编程
【5月更文挑战第5天】本文探讨了C语言中的多线程编程,包括多线程概念、通过POSIX线程库和Windows线程库的实现方式、基本步骤(创建、执行、同步、销毁线程)、线程同步机制(互斥锁、条件变量、信号量)以及优点(提高性能、增强并发处理、改善用户体验)。同时,文章指出多线程编程面临的挑战如线程安全、死锁和资源竞争,并提及内存管理问题。通过案例分析和展望未来趋势,强调了掌握多线程编程在提升程序效率和应对复杂任务中的重要性。
167 0
【C 言专栏】C 语言中的多线程编程
|
4月前
|
安全 Linux 编译器
从C语言到C++_40(多线程相关)C++线程接口+线程安全问题加锁(shared_ptr+STL+单例)(下)
从C语言到C++_40(多线程相关)C++线程接口+线程安全问题加锁(shared_ptr+STL+单例)
35 0