Linux和windows进程同步与线程同步那些事儿(三): Linux线程同步详解示例

2024-01-10 10:31:22

在Linux中,线程同步可以通过多种机制来实现,其中最常见的包括互斥锁(mutex)、条件变量(condition variable)和信号量(semaphore)。

1. 互斥锁(Mutex):

互斥锁是最常用的线程同步机制,它可以确保在同一时间只有一个线程可以访问共享资源。
在Linux中,可以使用pthread_mutex_t类型的互斥锁来实现线程同步。

代码示例:

#include <stdio.h>
#include <pthread.h>

int global_variable = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void* thread_function(void* arg) {
    for (int i = 0; i < 1000; i++) {
        // 加锁
        pthread_mutex_lock(&mutex);
        
        // 修改全局变量
        global_variable++;
        
        // 解锁
        pthread_mutex_unlock(&mutex);
    }
    
    return NULL;
}

int main() {
    pthread_t thread1, thread2;
    
    // 初始化mutex
    pthread_mutex_init(&mutex, NULL);
    
    // 创建两个线程
    pthread_create(&thread1, NULL, thread_function, NULL);
    pthread_create(&thread2, NULL, thread_function, NULL);
    
    // 等待线程结束
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    
    // 销毁mutex
    pthread_mutex_destroy(&mutex);
    
    printf("Global variable value: %d\n", global_variable);
    
    return 0;
}
 

2. 条件变量(Condition Variable):

条件变量用于线程间的通信和同步,允许线程等待某个特定条件的发生。
在Linux中,可以使用pthread_cond_t类型的条件变量来实现线程同步。

条件变量是一种同步机制,它允许线程在满足特定条件之前等待,并在条件满足时被其他线程通知。

示例代码:演示如何使用条件变量来控制多线程修改全局变量的值:

#include <stdio.h>
#include <pthread.h>

int global_var = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;


void* thread_function(void* arg)
{
    int new_value = *(int*)arg;
    
    pthread_mutex_lock(&mutex);
    
    // 等待条件满足
    while (global_var != 0)
    {
        pthread_cond_wait(&cond, &mutex);
    }
    
    // 修改全局变量的值
    global_var = new_value;
    
    printf("Thread %d has modified global_var to %d\n", pthread_self(), global_var);
    
    // 通知其他线程条件已经满足
    pthread_cond_broadcast(&cond);
    
    pthread_mutex_unlock(&mutex);
    
    return NULL;
}


int main()
{
    pthread_t thread1, thread2;
    int value1 = 123, value2 = 456;
    
    // 创建两个线程
    pthread_create(&thread1, NULL, thread_function, &value1);
    pthread_create(&thread2, NULL, thread_function, &value2);
    
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    
    printf("Final value of global_var is %d\n", global_var);
    
    return 0;
}

在上面的代码中,有两个线程分别调用thread_function函数。此函数接收一个整数参数作为新的全局变量值。线程首先获得互斥锁并进入临界区,然后使用pthread_cond_wait函数等待条件满足。只有当全局变量global_var的值为0时,线程才被允许修改这个变量的值。一旦满足这个条件,线程就会修改全局变量的值,并通过pthread_cond_broadcast函数通知其他等待这个条件的线程。最后,线程释放互斥锁并退出。

main函数中,我们创建了两个线程并等待它们完成。然后,我们打印最终的全局变量值。

通过使用条件变量,我们可以确保全局变量只能在满足特定条件时被修改,从而避免竞态条件和数据竞争的问题。

请注意,上述代码只是一个示例,用于说明如何使用条件变量来控制多线程修改全局变量的值。在实际的应用中,您可能还需要考虑其他方面,如错误处理和性能优化等。

3. 信号量(Semaphore):

信号量是一种经典的线程同步机制,它可以用于控制对共享资源的访问。
在Linux中,可以使用sem_t类型的信号量来实现线程同步。

在Linux下,我们可以使用信号量来实现对多线程修改全局变量的值的控制。信号量是一种用于进程间同步和互斥的机制,可以用来控制对共享资源的访问。

信号量可以分为二进制信号量计数信号量。二进制信号量只能取0或1,用于互斥操作。计数信号量可以取多个非负整数值,用于同步操作。

下面是一个简单的示例代码,实现了两个线程对全局变量进行自增操作的互斥控制:

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>

int global_var = 0;
sem_t sem;

void* thread_func(void* arg) {
    int i;
    for (i = 0; i < 100000; i++) {
        sem_wait(&sem); // 等待信号量,若为0则阻塞
        global_var++;
        sem_post(&sem); // 释放信号量,唤醒等待的线程
    }
    return NULL;
}

int main() {
    pthread_t thread1, thread2;

    sem_init(&sem, 0, 1); // 初始化信号量,初始值为1

    pthread_create(&thread1, NULL, thread_func, NULL);
    pthread_create(&thread2, NULL, thread_func, NULL);

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    sem_destroy(&sem); // 销毁信号量

    printf("Global variable value: %d\n", global_var);

    return 0;
}

在上面的示例中,我们首先通过 sem_init 函数初始化了一个计数信号量 sem,初始值为1。然后创建了两个线程,并通过 pthread_create 函数将其与 thread_func 函数绑定。thread_func 函数中通过 sem_wait 函数等待信号量,当信号量的值为0时则阻塞,否则减1并继续执行。在对全局变量进行自增操作后,通过 sem_post 函数释放信号量,唤醒等待的线程。最后,通过 sem_destroy 函数销毁信号量。

运行该程序,两个线程会对全局变量 global_var 进行自增操作,由于信号量的存在,每次只能有一个线程能够修改该变量,从而实现了对全局变量访问的互斥控制。最终输出的全局变量值应为 200000。

需要注意的是,信号量的使用需要谨慎,错误的使用可能导致死锁等问题。确保在必要的时候对信号量加锁和解锁,并根据实际需求选择合适的信号量类型。


这些线程同步机制都可以通过Linux提供的pthread库来使用。在实际编程中,选择合适的线程同步机制取决于具体的应用场景和需求,以确保线程间的安全访问和协调。


拓展:
在 Linux 中编译依赖 pthread 库的程序,可以使用以下命令行表达式来编译:

gcc -o output_file source_file.c -lpthread

其中,output_file 是编译后生成的可执行文件的文件名,source_file.c 是需要编译的源代码文件的文件名。

选项 -lpthread 表示链接 pthread 库,将其加入到编译过程中。

如果源文件有多个,可以将它们一一列出来,例如:

gcc -o output_file source_file1.c source_file2.c -lpthread

文章来源:https://blog.csdn.net/weixin_39568531/article/details/135494709
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。