进程与线程的区别和联系:
说明:以下代码用gcc编译时,需加上-lpthread选项。
什么是线程?
在程序中的一个执行流就叫做线程。一个进程至少包含一个线程。
进程与线程的区别:
- 进程是资源分配的最小单位
- 线程是程序执行的最小单位
进程与线程的联系:
- 线程间共享的进程数据:同一地址空间(代码段、数据段等)、文件描述符表、每种信号的处理方式、当前工作目录、用户ID和组ID等。
- 虽然线程间共享进程数据,但也拥有自己的一部分数据:线程ID、一组寄存器、私有栈、errno、信号屏蔽字、调度优先级等。

线程的优缺点:
优点:
- 创建一个线程的代价比创建一个进程的代价小的多。
- 线程所占用的资源要比进程少的多。
- 操作系统切换线程时所做的工作要比切换进程时所做的工作少的多。
- 能充分利用多处理器的可并行处理。
- 在等待慢速I/O操作完成的同时,程序可执行其他计算任务。
- 计算密集型应用,为了能在多处理器系统上运行,将计算任务分散到多个线程中实现。
- I/O密集型应用,为了提高性能,将I/O操作重叠,线程可以同时等待不同的I/O操作。
缺点:
- 如果计算密集型线程(不易被外部事件打断)的数量大于处理器的数量,那么可能增加同步与调度的开销。—性能损失
- 线程间的时序控制与公共资源的访问控制将会更加复杂,导致编写多线程程序难度增加。—健壮性降低
- 进程是访问控制的最小粒度,在一个线程中调用某些系统调用会对整个进程造成影响。—缺乏访问控制
线程控制:
线程创建:
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
- 说明:pthread_create()函数用于创建一个新线程,确定线程的入口函数。
- 参数:
- thread:线程ID,实质为mmap区域的一个地址,属于NPTL线程库的范围,线程库的后续操作就是根据此ID进行操作的。
- attr:线程的属性,一般置为NULL。
- start_routine:是一个函数指针,指向进程的入口函数,函数的参数为void ,返回值为void 。
- arg:入口函数的参数。
- 返回值:成功返回0,失败返回错误码。
注:pthread函数出错时不会设置全局变量errno,而是将错误码通过返回值返回。可以利用char *strerror(int errnum)函数将错误码解释为便于人类识别的字符错误信息。
线程终止:
线程的终止方式有三种:
- 从线程函数return,此方法不适合主线程,从主线程return默认会调用exit()函数退出整个进程。
- 调用pthread_exit()终止自己。
- 调用pthread_cancel()终止同一进程内的其他线程。
#include <pthread.h>
void pthread_exit(void *retval);
- 说明:retval指向线程的返回状态,函数没有返回值,线程结束时无法返回创建线程的地方。
注:return 和pthreead_exit()函数返回的指针,不能指向线程的局部变量,因为在线程结束时该局部变量已经销毁。
#include <pthread.h>
int pthread_cancel(pthread_t thread);
- 说明:thread为要杀死线程ID,成功返回0,失败返回错误码。
线程等待:
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
- 说明:pthread_join()函数以阻塞的方式等待thread指定的进程结束,当函数返回时,被等待线程的资源被回收,否则若不等待则可能出现类似僵尸进程的情况出现,造成资源泄露。
- 参数:
- thread:被等待的进程ID。
- retval:retval指向的空间存储被等待进程的返回值的地址,如果对返回值不感兴趣可以传NULL,thread线程以不同的方式终止,得到的返回值不同,总结如下:
1、如果thread线程通过return返回,则retval指向的空间存放thread线程的返回值地址。
2、如果thread线程调用pthread_exit()终止,则retval指向的空间存放pthread_exit()函数的参数的值。
3、如果thread线程被其他线程调用pthread_cancel()终止,则retval指向的空间存放的指针指向常量PTHREAD_CANCELED。
- 返回值:成功返回0,失败返回错误码。
线程分离:
#include <pthread.h>
int pthread_detach(pthread_t thread);
- 说明:当我们不关注线程的返回值时,等待线程是一种负担,此时可以用pthread_detach()将线程分离,当线程结束后,便不会再保留资源等待其他进程查看,而是自动释放掉所有资源,从而避免资源泄露。
- 参数:
- thread:要分离的进程ID,可以是线程组中的其他线程,也可以是自己本身。
- 返回值:成功返回0,失败返回错误码。
线程ID:
线程又叫轻量级进程,是操作系统调度的最小单位,在内核中每一个线程对应一个进程描述符(task_struct),如下:
struct task_struct{
pid_t pid; //线程ID,用于系统调度
pid_t tgid; //线程组ID(用户层的进程ID)
...
}
注:多线程的进程又称为线程组。线程组中第一个线程被称为主线程,主线程ID等与线程组ID(进程ID),在线程组中没有父子线程的概念,所有线程都是对等的。
| 用户态 | 系统调用 | task_struct中的字段 |
|---|---|---|
| 进程ID | getpid() | pid_t tgid |
| 线程ID | gettid() | pid_t pid |
在glibc中并没有提供gettid()的声明,使用者可以使用syscall来调用该函数,如下:
#include<sys/syscall.h>
pid_t tid = syscall(SYS_gettid);
上面接触到的线程ID,是操作系统调度时内核用来标识每一个线程的。与下面介绍的线程ID(pthread_t)不同,下面的线程ID属于NPTL的范畴,实质为mmap区的一个地址,NPTL库后续对线程的操作都是基于此线程ID的,可以通过pthread_self()函数获得线程自身ID。
#include <pthread.h>
pthread_t pthread_self(void);
在Linux系统中可以通过ps -eLf命令查看线程。
[DELL@MiWiFi-R1CL-srv]$ ps -eLf |head -1 && ps -eLf |grep grep
UID PID PPID LWP C NLWP STIME TTY TIME CMD
DELL 11250 4704 11250 0 1 23:40 pts/0 00:00:00 grep --color=auto grep
其中-L选项用于输出下面两个字段:
| 字段 | 含义 |
|---|---|
| LWP | 线程ID |
| NLWP | 线程组中线程数 |
线程的同步与互斥:
互斥锁(互斥量):
线程间需要共享一些变量,通过操作这些共享变量达到线程间交互的目的,但是当多个线程并发的操作这些共享变量时,便可能会出现数据的错乱,出现数据错乱的原因是操作共享资源的代码不是原子的,在一个线程操作共享资源的同时,执行流切换到另外一个线程也对共享资源进行操作,进而可能造成数据的紊乱,要解决此问题便要每个线程互斥的访问共享资源,一次只能由一个线程访问共享资源这种资源也叫做临界资源,对临界资源进行操作的代码叫做临界区。Linux中提供了互斥锁在线程访问临界区前先申请互斥锁,如果该锁没有被其他线程获得,则该进程获得锁,进入临界区,如果该锁已被其他进程获得,则该进程等待其他进程访问完临界区释放锁后才能获得互斥锁访问临界区,在该进程访问临界区的时候如有其他进程申请互斥锁,只能等待该进程访问完临界区才能获得互斥锁访问临界区。从而实现每个线程互斥的访问临界区的目的。
互斥量的使用步骤如下:
初始化:
互斥量的初始化有两种方法,静态初始化、动态初始化,如下:
- 静态初始化:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER - 动态初始化:
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
参数:
mutex:要初始化的互斥量。
attr:互斥量的属性设置,一般置为NULL,表示使用默认属性。
返回值:
成功返回0,失败返回错误码。
加锁解锁:
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);//加锁
int pthread_mutex_trylock(pthread_mutex_t *mutex);
//pthread_mutex_lock的非阻塞版本,当mutex被锁住的时候立即返回EBUSY
int pthread_mutex_unlock(pthread_mutex_t *mutex);//解锁
返回值:成功返回0,失败返回错误码。
销毁互斥量:
#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);
返回值:成功返回0,失败返回错误码。
利用互斥量实现售票系统:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>
int ticket = 100;
pthread_mutex_t mutex;
//pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *thread(void *arg){
char *p = (char *)arg;
while(1){
//加锁
pthread_mutex_lock(&mutex);
if(ticket > 0){
usleep(1000);
printf("%s sells ticket:%d\n",p,ticket);
ticket--;
//解锁
pthread_mutex_unlock(&mutex);
}else{
//解锁
pthread_mutex_unlock(&mutex);
break;
}
}
}
int main(){
//线程创建
pthread_t tid1,tid2,tid3,tid4;
pthread_mutex_init(&mutex,NULL);
pthread_create(&tid1,NULL,thread,"thread 1");
pthread_create(&tid2,NULL,thread,"thread 2");
pthread_create(&tid3,NULL,thread,"thread 3");
pthread_create(&tid4,NULL,thread,"thread 4");
//线程等待
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_join(tid3,NULL);
pthread_join(tid4,NULL);
//销毁互斥量
pthread_mutex_destroy(&mutex);
return 0;
}
结果:
[DELL@MiWiFi-R1CL-srv lesson13]$ ./pthread
thread 1 sells ticket:100
thread 1 sells ticket:99
thread 1 sells ticket:98
thread 1 sells ticket:97
...
thread 1 sells ticket:5
thread 1 sells ticket:4
thread 1 sells ticket:3
thread 1 sells ticket:2
thread 1 sells ticket:1
[DELL@MiWiFi-R1CL-srv lesson13]$
条件变量:
条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待“条件变量的条件成立”而挂起,另一个线程使“条件变量的条件成立”给等待线程发送信号激活等待线程。为了防止竞争,条件变量的使用总是和一个互斥锁一块使用。例如:在生产者-消费者模型中,当没有数据可以消费时,需要消费者等待生产者生产数据后才能继续消费,或者当生产的数据已经达到最大上限时,生产者不能再继续生产数据,必须等待消费者消费数据后才能继续生产数据。此时便需要用到条件变量。
条件变量的使用步骤如下:
初始化条件变量:
条件变量的初始化也分为两种,静态初始化、动态初始化:
静态初始化:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
动态初始化:
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
参数:
cond:要初始化的条件变量的指针。
attr:条件变量的属性,一般设置为NULL表示使用默认属性。
返回值:成功返回0,失败返回错误码。
等待条件满足:
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
说明:pthread_cond_wait()函数是一个原子操作,但会做3件事,1、对mutex进行解锁。2、阻塞式等待cond条件变量条件成立。3、对mutex进行加锁。
参数:
cond:在此条件变量上等待条件成立。
mutex:配合条件变量一块使用的互斥锁。
返回值:成功返回0,失败返回错误码。
条件变量等待使用示例:
pthread_mutex_lock(&mutex);
while(全局变量条件成立)/if(全局变量条件成立)
pthread_cond_wait(&cond,&mutex);
修改全局变量条件
pthread_mutex_unlock(&mutex);
说明:当只有一个线程等待条件变量时,可以使用if(),否则使用while(),使用while()可以避免使用if()时,pthread_cond_broadcast()函数发送的cond条件变量条件成立信号被另外一个线程先于本线程捕获并将全局变量条件修改为成立时,本线程直接修改全局变量条件造成的数据紊乱。
唤醒等待:
#include <pthread.h>
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
说明:pthread_cond_signal()函数用于给在cond条件变量上等待的线程中的一个线程发送条件变量条件成立的信号,激活等待线程。pthread_cond_broadcast()用于激活所有等待线程。
返回值:成功返回0,失败返回错误码。
条件变量唤醒使用示例:
pthread_mutex_lock(&mutex);
修改全局变量条件
pthread_cond_signal(&cond)/pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
销毁条件变量:
#include <pthread.h>
int pthread_cond_destroy(pthread_cond_t *cond);
参数:
cond:要销毁的条件变量。
返回值:成功返回0,失败返回错误码。
下面利用条件变量实现生产者消费者模型:
生产者消费者模型:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<time.h>
#define CONSUMER 2
#define PRODUCER 3
pthread_mutex_t mutex;
pthread_cond_t cond;
typedef struct msg{
int data;
struct msg* next;
} msg;
msg *head = NULL;
int count = 0;
//生产者
void *producer(void *arg){
int num = *(int *)arg;
free(arg);
for(;;){
pthread_mutex_lock(&mutex);
while(count == 10){
printf("%d begin wait producer a msg\n",num);
pthread_cond_wait(&cond,&mutex);
}
printf("%d end wait producer a msg\n",num);
printf("%d begin producer a msg\n",num);
msg *tmp = malloc(sizeof(msg));
tmp->data = rand()%100+1;
tmp->next = head;
head = tmp;
count++;
printf("count: %d\n",count);
printf("%d end producer a msg\n",num);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
sleep(rand()%1);
}
}
//消费者
void *consumer(void *arg){
int num = *(int *)arg;
free(arg);
for(;;){
pthread_mutex_lock(&mutex);
while(count == 0){
printf("%d begin wait a condition\n",num);
pthread_cond_wait(&cond,&mutex);
}
printf("%d end wait a condition\n",num);
printf("%d begin consumer a msg\n",num);
msg *tmp = head;
head = tmp->next;
count--;
printf("count: %d\n",count);
printf("%d end consumer a msg\n",num);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
free(tmp);
sleep(rand()%10);
}
}
int main(){
pthread_t tid[PRODUCER+CONSUMER];
srand(time(NULL));
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&cond,NULL);
int i = 0;
int *p = NULL;
for(i = 0;i < PRODUCER;i++){
p = (int *)malloc(sizeof(int));
*p = i;
pthread_create(&tid[i],NULL,producer,(void *)p);
}
for(;i < CONSUMER+PRODUCER;i++){
p = (int *)malloc(sizeof(int));
*p = i;
pthread_create(&tid[i],NULL,consumer,(void *)p);
}
for(i = 0;i < CONSUMER+PRODUCER;i++){
pthread_join(tid[i],NULL);
}
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}
结果:
[DELL@localhost lesson13]$ ./pthread
1 end wait producer a msg
1 begin producer a msg
count: 1
1 end producer a msg
0 end wait producer a msg
0 begin producer a msg
count: 2
0 end producer a msg
0 end wait producer a msg
0 begin producer a msg
count: 3
0 end producer a msg
0 end wait producer a msg
0 begin producer a msg
count: 4
0 end producer a msg
0 end wait producer a msg
0 begin producer a msg
count: 5
0 end producer a msg
0 end wait producer a msg
0 begin producer a msg
count: 6
0 end producer a msg
0 end wait producer a msg
0 begin producer a msg
count: 7
0 end producer a msg
0 end wait producer a msg
0 begin producer a msg
count: 8
0 end producer a msg
0 end wait producer a msg
0 begin producer a msg
count: 9
0 end producer a msg
0 end wait producer a msg
0 begin producer a msg
count: 10
0 end producer a msg
0 begin wait producer a msg
3 end wait a condition
3 begin consumer a msg
count: 9
3 end consumer a msg
0 end wait producer a msg
0 begin producer a msg
count: 10
...
POSIX信号量:
POSIX信号量与System V信号作用相同,都是用于同步操作,但POSIX信号量可以用于线程间的同步。
POSIX信号量的操作步骤如下:
初始化信号量:
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数:
- sem:要初始化的信号量。
- pshared:0表示信号量在线程间共享,非0表示信号量在进程间共享。
- value:信号量初始值,表示资源个数。
返回值:成功返回0,失败返回-1,并设置errno。
等待信号量:
#include <semaphore.h>
int sem_wait(sem_t *sem);
说明:原子操作,若sem的值大于0,则sem_wait()函数将sem的值减1,若sem的值等于0,则sem_wait()函数将阻塞式等待sem的值大于0再进行操作。
参数:
- sem:要执行-1的信号量。
返回值:成功返回0,失败返回-1,并设置errno。
发布信号量:
#include <semaphore.h>
int sem_post(sem_t *sem);
说明:原子操作,对sem的值+1。
返回值:成功返回0,失败返回-1,并设置errno。
销毁信号量:
#include <semaphore.h>
int sem_destroy(sem_t *sem);
返回值:成功返回0,失败返回-1,并设置errno。
用POSIX信号量实现生产者消费者模型:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<semaphore.h>
#define CONSUMER 2
#define PRODUCER 3
#define BUFFSIZE 10
int buffer[BUFFSIZE];
unsigned int consumer_index = 0;
unsigned int producer_index = 0;
unsigned int consumer_num = 0;
unsigned int producer_num = 0;
sem_t sem_full;
sem_t sem_empty;
pthread_mutex_t mutex;
pthread_t tid[PRODUCER+CONSUMER];
//生产者
void *producer(void *arg){
int num = *(int *)arg;
free(arg);
int i = 0;
while(1){
printf("%d wait buffer not full\n",num);
sem_wait(&sem_full);
pthread_mutex_lock(&mutex);
for(i = 0;i < BUFFSIZE;i++){
printf("%02d ",i);
if(i == producer_index){
printf("<--producer\n");
}else if(buffer[i] == -1){
printf("NULL\n");
}else{
printf("%d\n",buffer[i]);
}
}
printf("%d begin producer a msg:%u\n",num,producer_num);
buffer[producer_index] = producer_num++;
producer_index = (producer_index+1)%BUFFSIZE;
pthread_mutex_unlock(&mutex);
sem_post(&sem_empty);
usleep(1000);
}
return NULL;
}
//消费者
void *consumer(void *arg){
int num = *(int *)arg;
free(arg);
int i = 0;
while(1){
printf("%d wait buffer not empty\n",num);
sem_wait(&sem_empty);
pthread_mutex_lock(&mutex);
for(i = 0;i < BUFFSIZE;i++){
printf("%02d ",i);
if(i == consumer_index){
printf("<--consumer\n");
}else if(buffer[i] == -1){
printf("null\n");
}else{
printf("%d\n",buffer[i]);
}
}
consumer_num = buffer[consumer_index];
printf("%d begin consumer a msg:%d\n",num,consumer_num);
buffer[consumer_index] = -1;
consumer_index = (consumer_index+1)%BUFFSIZE;
pthread_mutex_unlock(&mutex);
sem_post(&sem_full);
sleep(1);
}
return NULL;
}
int main(){
pthread_mutex_init(&mutex,NULL);
sem_init(&sem_full,0,BUFFSIZE);
sem_init(&sem_empty,0,0);
int i = 0;
for(;i < BUFFSIZE;i++){
buffer[i] = -1;
}
int *p = NULL;
for(i = 0;i < PRODUCER;i++){
p = (int *)malloc(sizeof(int));
*p = i;
pthread_create(&tid[i],NULL,producer,(void *)p);
}
for(;i < PRODUCER+CONSUMER;i++){
p = (int *)malloc(sizeof(int));
*p = i;
pthread_create(&tid[i],NULL,consumer,(void *)p);
}
for(i = 0;i < CONSUMER+PRODUCER;i++){
pthread_join(tid[i],NULL);
}
pthread_mutex_destroy(&mutex);
sem_destroy(&sem_full);
sem_destroy(&sem_empty);
return 0;
}
结果:
[DELL@MiWiFi-R1CL-srv lesson13]$ ./pthread
1 wait buffer not full
0 wait buffer not full
2 wait buffer not full
3 wait buffer not empty
4 wait buffer not empty
00 <--producer
01 NULL
02 NULL
03 NULL
04 NULL
05 NULL
06 NULL
07 NULL
08 NULL
09 NULL
1 begin producer a msg:0
00 0
01 <--producer
02 NULL
03 NULL
04 NULL
05 NULL
06 NULL
07 NULL
08 NULL
09 NULL
0 begin producer a msg:1
00 <--consumer
01 1
02 null
03 null
04 null
05 null
06 null
07 null
08 null
09 null
3 begin consumer a msg:0
00 NULL
01 1
02 <--producer
03 NULL
04 NULL
05 NULL
06 NULL
07 NULL
08 NULL
09 NULL
2 begin producer a msg:2
00 null
01 <--consumer
02 2
03 null
04 null
05 null
06 null
07 null
08 null
09 null
...
读写锁:
在编写多线程程序时,经常会遇到有些公共数据读的相对较多,写的相对非常少,如果两个读者线程间也要互斥的访问公共数据,那么会对程序的效率有一定的影响,那么有没有一种方法可以使读者与写者线程之间互斥访问公共数据、写者与写者互斥访问公共数据、读者与读者共享访问公共数据呢?下面的读写锁就是解决这个问题的。
| 锁状态 | 读锁请求 | 写锁请求 |
|---|---|---|
| 无锁 | 成功 | 成功 |
| 读锁 | 成功 | 阻塞等待 |
| 写锁 | 阻塞等待 | 阻塞等待 |
读写锁的使用步骤:
初始化读写锁:
#include <pthread.h>
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
参数:
- rwlock:要初始化的读写锁。
- attr:读写锁的属性,一般置为NULL,表示使用默认属性。
返回值:成功返回0,失败返回错误码。
加锁:
#include <pthread.h>
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);//加读锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);//加写锁
返回值:成功返回0,失败返回错误码。
解锁:
#include <pthread.h>
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
返回值:成功返回0,失败返回错误码。
销毁读写锁:
#include <pthread.h>
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
返回值:成功返回0,失败返回错误码。
利用读写锁实现读者写者模型:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
pthread_rwlock_t rwlock;
int count = 0;
void *writethread(void *arg){
int num = *(int *)arg;
free(arg);
int tmp = 0;
while(1){
pthread_rwlock_wrlock(&rwlock);
tmp = count;
count++;
printf("write:%d:%#x:count = %d ++count = %d\n",num,pthread_self(),tmp,count);
pthread_rwlock_unlock(&rwlock);
sleep(2);
}
return NULL;
}
void *readthread(void *arg){
int num = *(int *)arg;
free(arg);
while(1){
pthread_rwlock_rdlock(&rwlock);
printf("read:%d:%#x:count = %d\n",num,pthread_self(),count);
pthread_rwlock_unlock(&rwlock);
sleep(1);
}
return NULL;
}
int main(){
pthread_rwlock_init(&rwlock,NULL);
pthread_t tid[8];
int i = 0;
int *p = NULL;
for(;i < 3;i++){
p = (int *)malloc(sizeof(int));
*p = i;
pthread_create(&tid[i],NULL,writethread,(void *)p);
}
for(;i < 8;i++){
p = (int *)malloc(sizeof(int));
*p = i;
pthread_create(&tid[i],NULL,readthread,(void *)p);
}
for(i = 0;i < 8;i++){
pthread_join(tid[i],NULL);
}
pthread_rwlock_destroy(&rwlock);
return 0;
}
结果:
[DELL@MiWiFi-R1CL-srv lesson13]$ ./pthread
write:0:0xf2962700:count = 0 ++count = 1
write:1:0xf2161700:count = 1 ++count = 2
write:2:0xf1960700:count = 2 ++count = 3
read:3:0xf115f700:count = 3
read:5:0xf015d700:count = 3
read:4:0xf095e700:count = 3
read:6:0xef95c700:count = 3
read:7:0xef15b700:count = 3
read:6:0xef95c700:count = 3
read:5:0xf015d700:count = 3
read:3:0xf115f700:count = 3
read:4:0xf095e700:count = 3
read:7:0xef15b700:count = 3
write:1:0xf2161700:count = 3 ++count = 4
write:2:0xf1960700:count = 4 ++count = 5
write:0:0xf2962700:count = 5 ++count = 6
read:6:0xef95c700:count = 6
read:5:0xf015d700:count = 6
read:3:0xf115f700:count = 6
read:4:0xf095e700:count = 6
read:7:0xef15b700:count = 6
read:3:0xf115f700:count = 6
read:5:0xf015d700:count = 6
read:4:0xf095e700:count = 6
read:6:0xef95c700:count = 6
read:7:0xef15b700:count = 6
^C
[DELL@MiWiFi-R1CL-srv lesson13]$
gdb调试多线程:
演示程序test.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
void *thread(void *arg){
int i = 0;
for(;i < 10;i++){
printf("%d ",i);
}
return NULL;
}
int main(){
pthread_t tid;
pthread_create(&tid,NULL,thread,NULL);
int j = 0;
for(;j < 5;j++){
printf("%d ",j);
}
pthread_join(tid,NULL);
printf("\n");
return 0;
}
编译程序:
gcc -g test.c -o test -lpthread
运行:
[DELL@MiWiFi-R1CL-srv lesson13]$ ./test
0 1 2 0 1 2 3 4 5 6 7 8 9 3 4
[DELL@MiWiFi-R1CL-srv lesson13]$
调试:
[DELL@MiWiFi-R1CL-srv lesson13]$ gdb test
...
Reading symbols from /home/DELL/work/lesson13/test...done.
(gdb) b main //在main函数入口设置断点
Breakpoint 1 at 0x40072f: file test.c, line 17.
(gdb) l //打印源代码
8 int i = 0;
9 for(;i < 10;i++){
10 printf("%d ",i);
11 }
12 return NULL;
13 }
14
15 int main(){
16 pthread_t tid;
17 pthread_create(&tid,NULL,thread,NULL);
(gdb) l //继续打印源代码
18 int j = 0;
19 for(;j < 5;j++){
20 printf("%d ",j);
21 }
22 pthread_join(tid,NULL);
23 printf("\n");
24 return 0;
25 }
(gdb) b 9 //在第九行设置断点
Breakpoint 2 at 0x400700: file test.c, line 9.
(gdb) b 19 //在第十九行设置断点
Breakpoint 3 at 0x400751: file test.c, line 19.
(gdb) r //运行程序
...
(gdb) info threads //查看当前线程数
Id Target Id Frame
* 1 Thread 0x7ffff7fd7740 (LWP 7987) "test" main () at test.c:17
(gdb) s //单步运行
[New Thread 0x7ffff77fb700 (LWP 7993)]
18 int j = 0; //到这一步已经创建了新线程
(gdb) s
Breakpoint 3, main () at test.c:19
19 for(;j < 5;j++){
(gdb) info threads //查看当前线程数,线程编号前有* 代表正在执行
Id Target Id Frame
2 Thread 0x7ffff77fb700 (LWP 7993) "test" thread (arg=0x0)
at test.c:9
* 1 Thread 0x7ffff7fd7740 (LWP 7987) "test" main () at test.c:19
(gdb) thread 2 //切换到2号线程
[Switching to thread 2 (Thread 0x7ffff77fb700 (LWP 7993))]
#0 0x00007ffff7871f60 in __GI__IO_file_stat () from /lib64/libc.so.6
(gdb) info threads
Id Target Id Frame
* 2 Thread 0x7ffff77fb700 (LWP 7993) "test" 0x00007ffff7871f60 in __GI__IO_file_stat () from /lib64/libc.so.6
1 Thread 0x7ffff7fd7740 (LWP 7987) "test" main () at test.c:19
(gdb)
注:在调试多线程程序时,主要用到info threads和thread [threadnumber]分别用于查看当前线程数以及切换程序的执行流到某一个线程。其余命令与调试进程类似。
本文介绍了进程与线程的区别和联系,详细阐述了线程的创建、终止、等待、分离及线程ID等概念,并深入探讨了线程间的同步机制,包括互斥锁、条件变量、POSIX信号量和读写锁的使用。
4万+

被折叠的 条评论
为什么被折叠?



