linux系统编程之线程

文章目录

什么是线程

一个进程在同一时刻只做一件事情。有了多个控制线程后,在程序设计时可以把进程设计成在同一时刻做不止一件事,每个线程各自处理独立的任务。进程是程序执行时的一个实例,是担当分配系统资源的基本单位。在面向线程设计的系统中,进程本身不是基本运行单位,而是线程的容器。线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程没有单独的地址空间,一个线程死掉会导致整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。与进程相比,线程相对来说比较简约,因为其是其他线程共享数据段,而且通信的机制也相对将简单。

API

多线程编程所需要的库,在linux上已经有pthread库支持,编程最基本包括了三点:线程的操作,互斥锁,条件。

线程操作

#include<pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
void pthread_exit(void *retval);
int pthread_join(pthread_t thread, void **retval);

①pthread_create:线程创建
thread: 是线程ID,当create成功后,ID号也被设置成功
attr:线程属性,一般可以设置NULL,以设置默认属性
start_routine:线程函数指针,参数类型是无类型指针
arg:是传递给线程的参数,如果想传多组数据,可以定义一个结构体
②pthread_join:线程等待,一直阻塞直到目标线程退出
thread:要等待的线程的ID
retval:会包含线程的返回码,如果不关心返回的状态可以设置为NULL
③pthread_exit:线程退出
retval:退出码

这里可以举个例子,注意这里编译时需要链库,-lpthread

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
void *func2(void * data){
        printf("in the thread2\n");
        sleep(2);
        printf("func2 quit!!!\n");
        pthread_exit(0);

}
void *func1(void * data){
        printf("in the thread1\n");
        printf("传递的参数是:%d\n",*(int *)data);
        printf("线程1 ID:%ld\n",pthread_self());
        sleep(2);
        printf("func1 quit!!!\n");

        pthread_exit(0);
}
int main(){
        pthread_t t1;
        pthread_t t2;
        int data=20;
        int *ret;

        pthread_create(&t1,NULL,func1,(void *)&data);
        pthread_create(&t2,NULL,func2,(void *)&data);

        printf("main:%ld\n",pthread_self());
        pthread_join(t1,(void*)&ret);
        pthread_join(t2,(void*)&ret);


        return 0;
}

linux系统编程之线程

互斥锁

#include<pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);

①pthread_mutex_init:创建互斥锁,动态创建
mutex互斥锁名称。
attr是互斥锁的属性,这里可以设置NULL为默认属性
如果想使用静态创建的方法,可以用一个宏PTHREAD_MUTEX_INITIALIZER,然后定义一个互斥锁为这个宏就可以不使用init函数
②pthread_mutex_destroy:销毁互斥锁
③pthread_mutex_lock:加互斥锁
④pthread_mutex_unlock:解互斥锁
⑤pthread_mutex_trylock:尝试解互斥锁
mutex是需要进行操作的互斥锁,当一个线程取到了锁,在解锁之前,另一个线程试图调用lock上锁,就会阻塞在那里。而如果另一个线程调用的不是lock而是trylock,这时会判断互斥量是否锁住,如果没有则锁住,如果上锁了,则上锁失败,但不会阻塞

死锁

何为死锁,当一个程序在运行的过程中,在多线程任务中,一个线程在对一个互斥量进行上锁后,在没有解锁该互斥量前,还准备对另一个互斥量进行上锁,而此时这个互斥量正在被第二个线程上锁,并且未解锁,这第二个线程也想对第一个线程中已上锁的互斥量进行上锁,就会两个线程同时阻塞在进程中,这时用trylock可以解决死锁。

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

pthread_mutex_t mutex1;
pthread_mutex_t mutex2;
void *func3(void * data){
		printf("in the thread3\n");
        pthread_mutex_lock(&mutex1);
        sleep(1);
        pthread_mutex_trylock(&mutex2);
		printf("func3 arrive!!!\n");
        pthread_mutex_unlock(&mutex1);
        pthread_mutex_unlock(&mutex2);
        }
void *func2(void * data){
		 printf("in the thread2\n");
        pthread_mutex_lock(&mutex2);
        sleep(1);
        pthread_mutex_trylock(&mutex1);
        printf("func2 arrive!!!\n");
        pthread_mutex_unlock(&mutex2);
        pthread_mutex_unlock(&mutex1);
}
         void *func1(void * data){
        printf("in the thread1\n");
        printf("传递的参数是:%d\n",*(int *)data);
}
int main(){
        pthread_t t1;
        pthread_t t2;
        pthread_t t3;
        int data=20;
        int *ret;

        pthread_mutex_init(&mutex1,NULL);
        pthread_mutex_init(&mutex2,NULL);
        pthread_create(&t1,NULL,func1,(void *)&data);
        pthread_create(&t2,NULL,func2,(void *)&data);
        pthread_create(&t3,NULL,func3,(void *)&data);

        printf("main:%ld\n",pthread_self());
        pthread_join(t1,(void*)&ret);
        pthread_join(t2,(void*)&ret);
        pthread_join(t3,(void*)&ret);
        pthread_mutex_destroy(&mutex1);
        pthread_mutex_destroy(&mutex2);


        return 0;
}


linux系统编程之线程
从代码可以看到,这里准备是想强制让其发生死锁的现象,但是因为用的是trylock,所以线程并不会阻塞

条件

#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, cond struct timespec *restrict ti
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);

①pthread_cond_init:创建条件变量,动态创建
cond:条件变量名,attr条件的属性,这里依旧可以设置为NULL以默认属性来创建
也可以用一个宏PTHREAD_COND_INITIALIZER来静态创建,当使用了这个宏后,init函数就可以不需要了
②pthread_cond_destroy:销毁条件变量
③pthread_cond_wait:等待发送另一个线程发送信号,否则该线程阻塞
④pthread_cond_timedwait:与wait类似,但是有一个定时time
⑤pthread_cond_signal:唤醒满足该条件的某个线程
⑥pthread_cond_broadcast:唤醒满足该条件的所有线程,如果要唤醒所有线程,那么正在等待的线程中的互斥锁需要都不一样,否则和signal函数的效果一样,只能唤醒单个线程。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
pthread_mutex_t mutex1;
pthread_mutex_t mutex2;
pthread_cond_t cond;
void *func3(void * data){
        printf("in the thread3\n");
        pthread_cond_wait(&cond,&mutex1);
        printf("func3 arrive!!!\n");
}
void *func2(void * data){
        printf("in the thread2\n");
        pthread_cond_wait(&cond,&mutex2);
        printf("func2 arrive!!!\n");
}
void *func1(void * data){
        printf("in the thread1\n");
        printf("传递的参数是:%d\n",*(int *)data);
        printf("线程1:%ld\n",pthread_self());
        pthread_mutex_lock(&mutex1);
        pthread_mutex_lock(&mutex2);
    	sleep(3);
        pthread_cond_broadcast(&cond);
        pthread_mutex_unlock(&mutex1);
        pthread_mutex_unlock(&mutex2);
}
int main(){
        pthread_t t1;
        pthread_t t2;
        pthread_t t3;
        int data=20;
        int *ret;

        pthread_mutex_init(&mutex1,NULL);
        pthread_mutex_init(&mutex2,NULL);
        pthread_cond_init(&cond,NULL);
        pthread_create(&t1,NULL,func1,(void *)&data);
        pthread_create(&t2,NULL,func2,(void *)&data);
        pthread_create(&t3,NULL,func3,(void *)&data);

        printf("main:%ld\n",pthread_self());
        pthread_join(t1,(void*)&ret);
        pthread_join(t2,(void*)&ret);
        pthread_join(t3,(void*)&ret);
        pthread_mutex_destroy(&mutex1);
        pthread_mutex_destroy(&mutex2);

        pthread_cond_destroy(&cond);

        return 0;
}


linux系统编程之线程
这里可以看到当线程一中有两个互斥锁,两个待唤醒线程中对应的互斥锁也不一样,所以才能同时一起唤醒

上一篇:关于线程安全的那些事


下一篇:pthread_cond_wait