线程的基本概念:
1.线程在Linux下是轻量级的进程,同一程序中的线程会共享同一份全局内存区,包括代码段(.text),数据段(.bss,.data),堆段;但是每一个线程都有自己独立的栈段。所有的线程栈都驻留在同一块虚拟内存。
线程的基本API
启动程序时产生的进程是单条线程,也叫主线程
1.创建线程
#1:传出参数,表示线程的id号
#2:线程的基本属性(唯一接触到的是分离属性后面讲解)
#3:线程创建后所要执行的函数,该函数返回值为void* 不能够接受成员函数除非是静态成员函数(不包含this指针)
#4:该函数传入的参数,可以是结构体
注意:图中编译和链接需要指定参数 eg: gcc -o create pthread_create.c -pthread
2.退出线程
#1:该参数是一个传入参数,如果你想赋予该线程退出状态;但是必须是一个全局变量,否者如果线程返回,局部变量空间将释放,获取到的线程退出状态未知。
不论是主线程还是子线程,都是平等地位,任意一方调用该函数退出(不包括任意一个线程调用exit()或在main()中调用return),其余的线程将正常运行。
3.回收线程
#1:要释放的线程的id(不能释放任意的线程,只能是你知道线程id)
#2:传出参数,获取所链接线程的退出状态。
线程之间平等,任意双方都可以互相join,如果未能join那么线程退出后将变成僵尸线程,浪费系统资源。
4.设置分离属性
我们应当在线程一创建就将其设置为分离的(主线程与新线程没有关系),这样新创建的线程就可以自动释放,并且不能够再调用pthread_join();
线程同步
1.线程的安全问题
线程间虽然通信方便但是可能会导致多个线程访问同一变量,引出临界区:某一共享资源的代码片段。造成线程同步机制的本质就是线程在同一块虚拟内存空间中工作。
1.静态的互斥锁变量
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
2.动态创建锁变量
pthread_mutex_init();
#1:操作的锁变量
#2:锁变量的属性,如果为NULL则是默认属性
pthread_mutex_destory();
3.加锁与解锁
pthread_mutex_lock(&mtx);
pthread_mutex_unlock(&mtx);
2.互斥量的死锁
如果一个线程同时访问两个或多个临界区资源,同时有两个或多个线程操作锁变量,可能产生死锁。
一个线程锁住mtx1,但是需要mtx2;另一个线程同时锁住mtx2,但是需要mtx1,这两个线程将无限期的等待下去。
解决方法:
1.按层级操作锁变量,任何一个线程必须先锁mtx1,再锁mtx2。这样不会产生死锁
2.无需遵循层级规则,当一个线程锁住mtx1时,调用pthread_mutex_trylock();如果返回错误,释放所有的锁变量。
1 #include<pthread.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 #include<stdio.h>
5
6 int number = 0;
7 void printids(const char* s)
8 {
9 pid_t pid;
10 pthread_t tid;
11 pid = getpid();
12 tid = pthread_self();
13 printf("%s pid %lu tid %lu\n",s,pid,tid);
14 }
15
16 void* thr_fn()
17 {
18 printids("new tids:");
19 return ((void*)0);
20 }
21
22 int main()
23 {
24 pthread_t ntid;
25 int err = 0;
26
27 err = pthread_create(&ntid, NULL, thr_fn, NULL);
28 if(err != 0)
29 {
30 perror( "can't create thread");
31 }
3.条件变量
条件变量与互斥锁不同,实质上它并不像锁,而是在不满足条件的时候阻塞线程,满足条件时解除阻塞,互斥锁是保护临界区,每次只能由一个线程访问。一般情况下条件变量和互斥锁共同使用。(就不列出条件变量api)
生产者:
消费者:
线程安全
使用了所有线程共享的全局变量或静态变量,但是调用函数所访问的变量是安全的。实现线程安全方法:
1.将互斥量与函数关联起来:实现简单但是会导致多线程访问函数是串行访问,丧失并发性
2.当函数访问临界区的时候增加互斥量,除非多个线程同时执行临界区代码,否者是并发的。
可以通过调用函数自己定义的缓存区来存储每次的结果,达到线程安全的目的。