线程同步以及线程间通信是多线程编程中必不可少的概念。本文将详细介绍线程同步和线程间通信的相关知识。
一、线程同步
在多线程编程中,当多个线程同时访问共享资源时,会产生竞争条件(Race Condition),导致程序出现错误或异常情况。线程同步机制是为了解决这个问题而设计的。其基本思想是通过控制对共享资源的访问,使得它们能够安全地被多个线程共享。下面介绍三种常用的线程同步机制。
- 互斥锁
互斥锁(Mutex)是最常用的线程同步技术之一,它保证在同一时间只有一个线程可以访问共享资源。当一个线程获得了互斥锁后,其他线程必须等待该线程释放锁才能获得。
在 Linux C++ 中,使用 pthread_mutex_t 数据类型来表示互斥锁。下面是一个互斥锁的例子:
#include <pthread.h>
#include <iostream>
using namespace std;
pthread_mutex_t mutex; //定义互斥锁
void *thread_function(void *arg)
{
int i;
for (i = 0; i < 5; i++)
{
pthread_mutex_lock(&mutex); //加锁
cout << "Thread " << pthread_self() << " is running\n";
pthread_mutex_unlock(&mutex); //释放锁
}
return NULL;
}
int main()
{
pthread_t thread1, thread2;
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);
pthread_mutex_destroy(&mutex); //销毁互斥锁
return 0;
}
- 条件变量
条件变量(Condition Variable)是一种线程同步机制,用于线程之间的通信。它允许某个线程等待另外一个线程的通知,而无需使用忙等待(Busy Waiting)的方式来等待。
在 Linux C++ 中,使用 pthread_cond_t 数据类型来表示条件变量。下面是一个条件变量的例子:
#include <pthread.h>
#include <iostream>
using namespace std;
pthread_mutex_t mutex; //定义互斥锁
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; //初始化条件变量
void *thread_function(void *arg)
{
cout << "Thread " << pthread_self() << " waiting...\n";
pthread_mutex_lock(&mutex); //加锁
pthread_cond_wait(&cond, &mutex); //等待条件变量
cout << "Thread " << pthread_self() << " is running\n";
pthread_mutex_unlock(&mutex); //释放锁
return NULL;
}
int main()
{
pthread_t thread;
pthread_mutex_init(&mutex, NULL); //初始化互斥锁
pthread_create(&thread, NULL, thread_function, NULL);
sleep(2); //等待两秒钟
cout << "Signal...\n"; //发出通知信号
pthread_mutex_lock(&mutex); //加锁
pthread_cond_signal(&cond); //发出条件变量信号
pthread_mutex_unlock(&mutex); //释放锁
pthread_join(thread, NULL);
pthread_mutex_destroy(&mutex); //销毁互斥锁
pthread_cond_destroy(&cond); //销毁条件变量
return 0;
}
- 读写锁
读写锁(Reader-Writer Lock)是一种特殊的互斥锁,用于解决多读单写场景下的性能问题。读写锁允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。
在 Linux C++ 中,使用 pthread_rwlock_t 数据类型来表示读写锁。下面是一个读写锁的例子:
#include <pthread.h>
#include <iostream>
using namespace std;
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; //初始化读写锁
void *reader_function(void *arg)
{
pthread_rwlock_rdlock(&rwlock); //以读模式加锁
cout << "Reader " << pthread_self() << " is reading...\n";
sleep(2);
pthread_rwlock_unlock(&rwlock); //释放锁
return NULL;
}
void *writer_function(void *arg)
{
pthread_rwlock_wrlock(&rwlock); //以写模式加锁
cout << "Writer " << pthread_self() << " is writing...\n";
sleep(2);
pthread_rwlock_unlock(&rwlock); //释放锁
return NULL;
}
int main()
{
pthread_t reader1, reader2, writer;
pthread_create(&reader1, NULL, reader_function, NULL);
pthread_create(&reader2, NULL, reader_function, NULL);
pthread_create(&writer, NULL, writer_function, NULL);
pthread_join(reader1, NULL);
pthread_join(reader2, NULL);
pthread_join(writer, NULL);
pthread_rwlock_destroy(&rwlock); //销毁读写锁
return 0;
}
二、线程间通信
线程间通信(Inter-Thread Communication)是指多个线程之间通过某种方式来交换信息,并协同完成某个任务。下面介绍三种常用的线程间通信机制。
- 管道
管道(Pipe)是一种基于文件描述符的进程间通信方式,也可以被应用于线程间通信。在 Linux C++ 中,管道由两端组成,分别是读端和写端。一个线程向管道中写入数据,另一个线程从管道中读取数据。
下面是一个管道的例子:
#include <unistd.h>
#include <iostream>
using namespace std;
int pipefd[2];
void *write_function(void *arg)
{
char buffer[] = "Hello, world!";
write(pipefd[1], buffer, sizeof(buffer)); //向管道中写入数据
return NULL;
}
void *read_function(void *arg)
{
char buffer[256];
read(pipefd[0], buffer, sizeof(buffer)); //从管道中读取数据
cout << "Receive data: " << buffer << endl;
return NULL;
}
int main()
{
pthread_t thread1, thread2;
pipe(pipefd); //创建管道
pthread_create(&thread1, NULL, write_function, NULL);
pthread_create(&thread2, NULL, read_function, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
close(pipefd[0]); //关闭管道
close(pipefd[1]);
return 0;
}
- 共享内存
共享内存(Shared Memory)是一种进程间通信方式,也可以被用于线程间通信。它指多个线程可同时访问的一块系统内存。
在 Linux C++ 中,可以使用 shmget、shmat 和 shmdt 等函数来操作共享内存。下面是一个共享内存的例子:
#include <sys/shm.h>
#include <iostream>
using namespace std;
const int SHM_SIZE = 1024;
int main()
{
int shmid;
char *shmaddr;
shmid = shmget(IPC_PRIVATE, SHM_SIZE, IPC_CREAT|0600); //创建一块共享内存
shmaddr = (char*)shmat(shmid, NULL, 0); //将共享内存映射至进程的地址空间
strcpy(shmaddr, "Hello, world!"); //向共享内存中写入数据
cout << "Data in shared memory: " << shmaddr << endl;
shmdt((void*)shmaddr); //将共享内存从进程的地址空间中解除映射
shmctl(shmid, IPC_RMID, NULL); //销毁共享内存
return 0;
}
- 消息队列
消息队列(Message Queue)是一种进程间通信方式,也可以被用于线程间通信。它通过一个由操作系统维护的消息队列来传递数据。发送线程向消息队列中写入数据,接收线程从消息队列中读取数据。
在 Linux C++ 中,可以使用 msgget、msgsnd 和 msgrcv 等函数来操作消息队列。下面是一个消息队列的例子:
#include <sys/ipc.h>
#include <sys/msg.h>
#include <iostream>
using namespace std;
const int MSG_SIZE = 1024;
struct Message
{
long mtype;
char mtext[MSG_SIZE];
};
int main()
{
key_t key;
int msgid;
Message message;
key = ftok(".", 'a'); //生成一个键值
msgid = msgget(key, IPC_CREAT|0600); //创建消息队列
message.mtype = 1; //设置消息类型
strcpy(message.mtext, "Hello, world!"); //设置消息内容
msgsnd(msgid, &message, sizeof(message), 0); //发送消息
msgrcv(msgid, &message, sizeof(message), 0, 0); //接收消息
cout << "Receive data: " << message.mtext << endl;
msgctl(msgid, IPC_RMID, NULL); //销毁消息队列
return 0;
}