Linux线程同步必知,常用方法揭秘!,如果线程之间没有正确


一、为什么要线程同步

在Linux 多线程编程中,线程同步是一个非常重要的问题。如果线程之间没有正确地同步,就会导致程序出现一些意外的问题,例如:

  1. 竞态条件(Race Condition):多个线程同时修改同一个共享变量,可能会导致不可预测的结果,因为线程的执行顺序是不确定的。
  2. 死锁(Deadlock):当两个或多个线程互相等待对方释放资源时,可能会导致死锁,这会导致程序无法继续执行。
  3. 活锁(Livelock):当多个线程相互响应对方的动作,而没有任何进展时,可能会导致活锁,这也会导致程序无法继续执行。

  • 两个人在走路时需要相互让路,两个人都想让对方先通过,但最终还是没有人通过,这就是一种活锁情况

接下来将介绍互斥锁、条件变量、信号量、读写锁这几种线程同步方法,并使用C语言代码示例说明其使用方法。

二、互斥锁

互斥锁是一种用于线程同步的锁,用于保护共享资源。只有拥有该锁的线程才能访问共享资源,其他线程需要等待锁被释放后才能继续执行。

在Linux环境下,我们可以使用pthread库提供的互斥锁函数来实现互斥锁机制。以下是一些常用的互斥锁函数:

函数名 描述
pthread_mutex_init 初始化互斥锁
pthread_mutex_lock 加锁互斥锁
pthread_mutex_trylock 尝试加锁互斥锁
pthread_mutex_unlock 解锁互斥锁
pthread_mutex_destroy 销毁互斥锁

初始化互斥锁

在使用互斥锁之前,需要先初始化互斥锁。pthread_mutex_init函数用于初始化一个互斥锁。函数原型如下:

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

其中,mutex参数是一个指向pthread_mutex_t结构体的指针,用于指定要初始化的互斥锁;attr参数是一个指向pthread_mutexattr_t结构体的指针,用于指定互斥锁的属性,通常设置为NULL。

以下是一个初始化互斥锁的例子:

#include <pthread.h>

pthread_mutex_t mutex;

int main()
{
    // 初始化互斥锁
    pthread_mutex_init(&mutex, NULL);
    
    // ...
    
    // 销毁互斥锁
    pthread_mutex_destroy(&mutex);
    
    return 0;
}

加锁互斥锁

加锁互斥锁用于保证同一时刻只有一个线程能够访问共享资源。pthread_mutex_lock函数用于加锁一个互斥锁。函数原型如下:

int pthread_mutex_lock(pthread_mutex_t *mutex);

其中,mutex参数是一个指向pthread_mutex_t结构体的指针,用于指定要加锁的互斥锁。

以下是一个加锁互斥锁的例子:

#include <pthread.h>

pthread_mutex_t mutex;

void* thread_func(void* arg)
{
    // 加锁互斥锁
    pthread_mutex_lock(&mutex);
    
    // 访问共享资源
    // ...
    
    // 解锁互斥锁
    pthread_mutex_unlock(&mutex);
    
    return NULL;
}

int main()
{
    // 初始化互斥锁
    pthread_mutex_init(&mutex, NULL);
    
    // 创建线程
    pthread_t tid;
    pthread_create(&tid, NULL, thread_func, NULL);
    
    // ...
    
    // 等待线程结束
    pthread_join(tid, NULL);
    
    // 销毁互斥锁
    pthread_mutex_destroy(&mutex);
    
    return 0;
}

尝试加锁互斥锁

尝试加锁互斥锁与加锁互斥锁的主要区别在于,如果互斥锁已经被其他线程锁定了,尝试加锁互斥锁将不会阻塞当前线程,而是会立即返回一个错误代码。函数原型如下:

int pthread_mutex_trylock(pthread_mutex_t *mutex);

其中,mutex参数是一个指向pthread_mutex_t结构体的指针,用于指定要尝试加锁的互斥锁。

以下是一个尝试加锁互斥锁的例子:

#include <pthread.h>

pthread_mutex_t mutex;

void* thread_func(void* arg)
{
    // 尝试加锁互斥锁
    int ret = pthread_mutex_trylock(&mutex);
    if (ret == 0) {
        // 访问共享资源
        // ...
        
        // 解锁互斥锁
        pthread_mutex_unlock(&mutex);
    } else {
        // 互斥锁已经被其他线程锁定了
        // ...
    }
    
    return NULL;
}

int main()
{
    // 初始化互斥锁
    pthread_mutex_init(&mutex, NULL);
    
    // 创建线程
    pthread_t tid;
    pthread_create(&tid, NULL, thread_func, NULL);
    
    // ...
    
    // 等待线程结束
    pthread_join(tid, NULL);
    
    // 销毁互斥锁
    pthread_mutex_destroy(&mutex);
    
    return 0;
}

解锁互斥锁

解锁互斥锁用于释放已经锁定的互斥锁。pthread_mutex_unlock函数用于解锁一个互斥锁。函数原型如下:

int pthread_mutex_unlock(pthread_mutex_t *mutex);

其中,mutex参数是一个指向pthread_mutex_t结构体的指针,用于指定要解锁的互斥锁。

以下是一个解锁互斥锁的例子:

#include <pthread.h>

pthread_mutex_t mutex;

void* thread_func(void* arg)
{
    // 加锁互斥锁
    pthread_mutex_lock(&mutex);
    
    // 访问共享资源
    //
	// 解锁互斥锁
	pthread_mutex_unlock(&mutex);

	return NULL;
}

销毁互斥锁

在不再需要使用互斥锁时,需要将互斥锁销毁。pthread_mutex_destroy函数用于销毁一个互斥锁。函数原型如下:

int pthread_mutex_destroy(pthread_mutex_t *mutex);

其中,mutex参数是一个指向pthread_mutex_t结构体的指针,用于指定要销毁的互斥锁。

以下是一个销毁互斥锁的例子:

#include <pthread.h>

pthread_mutex_t mutex;

int main()
{
    // 初始化互斥锁
    pthread_mutex_init(&mutex, NULL);
    
    // ...
    
    // 销毁互斥锁
    pthread_mutex_destroy(&mutex);
    
    return 0;
}

示例程序

下面是一个简单的示例程序,演示了如何使用互斥锁来同步两个线程的访问。

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

pthread_mutex_t mutex;
int shared_data = 0;

void *thread_func(void *arg)
{
    int i;
    for (i = 0; i < 1000000; i++) {
        pthread_mutex_lock(&mutex);
        shared_data++;
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

int main()
{
    pthread_t thread1, thread2;
    pthread_mutex_init(&mutex, NULL);
    pthread_create(&thread1, NULL, thread_func, NULL);
    pthread_create(&thread2, NULL, thread_func, NULL);
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    pthread_mutex_destroy(&mutex);
    printf("Shared data: %d\n", shared_data);
    return 0;
}

在这个程序中,thread_func函数是两个线程执行的函数,它会对shared_data变量进行1000000次加一操作。

为了确保多个线程不会同时访问shared_data变量,我们使用了一个互斥锁。当一个线程要访问shared_data变量时,它会调用pthread_mutex_lock函数来加锁。如果锁已经被其他线程持有,那么这个线程就会被阻塞,直到锁被释放为止。当线程完成对shared_data变量的操作后,它会调用pthread_mutex_unlock函数来释放锁。

在这个程序执行完毕后,我们可以通过打印shared_data变量的值来检查程序是否正确地同步了两个线程的访问。如果程序正确地同步了线程的访问,那么shared_data变量的值应该是2000000。

相关内容