Thread 概念 线程: 有时候又称为轻量级进程, 程序执行的最小单位, 系统独立调度和分配cpu的最小基本单位, 他是进程中的一个实体. 一个进程中可以有多个线程,这些线程共享进程的所有资源,线程本身只包含一点必不可少的资源。
进程退出出现了很多弊端, 一是由于进程是资源的拥有者,创建,撤销与切换存在较大的时空开销,因此需要引入轻型进程, 而是由于对称多处理(smp)出现,可以满足多个运行单位,而多个进程并行开销过大。
并发 并发是指同一时刻,只能有一条指令执行, 但是多个进程指令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果,看起来同时发生,单核
并行 并行是指在同一时刻,有多条指令在多处理器上同时执行,真正的同时发生
同步 彼此依赖关系的调用不应该”同时发生“,而同步就是要阻止那些”同时发生“的事情(比如数据库操作需要)
异步 异步的概念和同步是相对的,任何两个彼此独立的操作是异步的,它表明独立的发生
多线程的优势 1 在多处理器开发程序的并行性
2 在等待慢速IO操作时,程序可以执行其他操作,提高并发性
3 模块化的编程,能更清晰的表达程序中独立事件的关系,结构清晰
4 占用较少的系统资源
(注:多线程不一定要多核处理器)
线程的生命周期 创建线程 线程 | 进程
标识符类型 pthread_t pid_t
获取id pthread_self() getpid()
创建 pthread_create() fork()
pthread_t: 结构体(FreeBSD5.2, Mac Os10.3) / unsinged long int (linux /usr/include/bits/pthreadtypes.h中定义)
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
arg1: 传入储存thread_id的地址
arg2: 属性, 为NULL时为默认属性
arg3: 将要运行的函数地址(要为静态函数)
arg4: 传入的参数
If attr is NULL, then the thread is created with default attributes.
初始线程/主线程
在c程序运行时,首先运行main函数。在线程代码中,这个特殊的执行流被称作初始线程或者主线程。
住线程的特殊性在于,它在main函数返回的时候,会导致进程结束,进程内人所有的线程也会结束。这可不是一个好的现象,可以在主线程中调用pthread_exit函数,这样进程就会等待所有线程结束时才会终止。
主线程接收参数的方式是argc和argv,而普通的线程只有void*
在绝大多数情况下,主线程在默认堆栈上运行,这个堆栈可以增长到足够的长度,而普通线程的堆栈是受限制的,一旦溢出就会产生错误
创建线程
主线程是随着进程的创建而创建的
其他线程可以通过调用函数来创建,主要调用pthread_create
注意,新线程可能在当前线程的pthread_create函数返回之前就已经运行了,甚至可能运行完毕
线程的四个基本状态 就绪 线程能够运行,但是在等待可用的处理器
当线程刚被创建的时候就处于就绪状态, 或者当线程被解除阻塞以后也会处于就绪状态。就绪的线程在等待一个可用的处理器,当一个运行的线程被抢占时,它立刻又返回就绪状态
运行 线程在运行中,在多核系统中,可能同时有多个线程在运行。
当处理器选择一个就绪的线程执行时,它立刻变为运行状态
阻塞 线程在等待处理器中以外的其他条件
线程会在以下情况下发生阻塞: 试图加锁一个已经被锁住的互斥量,等待条件变量,调用singwait等待尚未发生的信号,执行无法完成的I/O信号,由于内存页错误
终止 线程从启动函数中返回, 或者调用ptrehad_exit函数,或则被取消
线程通常启动函数中返回终止自己,或者调用pthread_exit退出,或者取消线程
回收 线程的分离属性:
分离一个正在运行的线程并不影响它,仅仅是通知当前系统该线程结束时,其所属的资源可以回收。一个没有分离的线程在终止时会保留它的虚拟内存, 包括他们的堆栈和其他系统资源, 有时这种线程被称为“僵尸线程”。创建线程时默认是非分离的
如果线程具有分离属性,线程终止时会被立刻回收,但是你必须释放由该线程占有的程序资源。有malloc或者mmap分配的虚拟内存可以在任何时候由任何线程释放, 条件变量,互斥量,信号灯可以由任何线程销毁,只要他们被解锁了或者没有线程等待。但是只有互斥量的主人才能解锁它,所以在线程终止前,你需要解锁互斥量。
线程的基本控制
终止
连接
退出
清理
线程的终止 exit是危险的 如果进程中任意一个线程调用了eixt, _Exit, _exit, 那么整个进程就会终止
终止进程的方式 普通的单个线程有以下3中方式退出, 这样不会终止进程
从启动历程中返回, 返回值是线程的退出码
线程可以被同一进程中的其他线程取消
线程调用pthread_exit(void *rval)函数, rval是退出码
return 和 pthread_exit()的区别
线程的连接 1 2 3 4 5 #include <pthread.h> int pthread_join (pthread_t thread, void **retval) ;
调用该函数的线程会一直阻塞,直到指定的线程tid调用pthread_exit, 从启动历程返回或者被取消
调用pthread_join会使指定的线程处于分离状态,如果制定线程已经处于分离状态,那么调用就会失败
phtread_detach可以分离一个线程, 一个线程被成功join后,其他线程就不能调用pthread_join连接指定的tid线程了.
例子 获取线程id 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <stdio.h> #include <pthread.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main (void ) { pid_t pid; pthread_t tid; pid = getpid(); tid = pthread_self(); printf ("pid: %x pthread_id: %lx\n" , pid, tid); return 0 ; }
创建线程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include <pthread.h> #include <unistd.h> #include <stdio.h> void print_id (char *arg) { pid_t pid; pthread_t tid; pid = getpid(); tid = pthread_self(); printf ("%s pid: %lx tid: %lx\n" ,arg, pid, tid); }void *thread_fun (void *arg) { print_id(arg); int a; printf ("\n stack: %p\n" , &a); return (void *)0 ; }int main (void ) { pthread_t ntid; int err; err = pthread_create(&ntid, NULL , thread_fun, "new thread" ); if (err != 0 ) { printf ("create new thread failed\n" ); return -1 ; } print_id("main_thread" ); int a; printf ("\n stack: %p\n" , &a); sleep(2 ); return 0 ; }
实现创建线程传入多个参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <pthread.h> #include <stdio.h> #include <string.h> #include <unistd.h> struct Student { int age; char name[64 ]; };void *thread_fun (void *std ) { printf ("age: %d name %s\n" , ((struct Student *)std )->age, ((struct Student *)std )->name); return (void *)0 ; }int main (int argc, char **argv) { if (argc > 1 ) { if (!strcmp (argv[1 ], "exit" )) { return 0 ; } } struct Student std ; std .age = 16 ; strcpy (std .name, "Hello World" ); pthread_t tid; int err; err = pthread_create(&tid, NULL , thread_fun, &std ); if (err != 0 ) { printf ("create trhead fail!\n" ); return -1 ; } int retval; pthread_exit(&retval); printf ("main thread exit\n" ); return 0 ; }
采用变量来实现同步 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 #include <stdio.h> #include <pthread.h> #include <errno.h> struct Value { int thread; int value; int lock; };void *fun (void *arg) { struct Value *value = (struct Value *)arg; while (value->value < 50 ) { if (value->lock == 0 && value->thread == 1 ) { value->lock = 1 ; printf ("child: %d\n" , value->value); value->value += 1 ; value->thread = 0 ; value->lock = 0 ; } } return (void *)0 ; }int main (void ) { struct Value value ; value.value = 0 ; value.lock = 0 ; value.thread = 0 ; int err, retval; pthread_t tid; err = pthread_create(&tid, NULL , fun, &value); if (err != 0 ) { perror("thread_create:" ); return -1 ; } while (value.value < 50 ) { if (value.lock == 0 && value.thread == 0 ) { value.lock = 1 ; printf ("main_thread: %d\n" , value.value); value.value += 1 ; value.thread = 1 ; value.lock = 0 ; } } pthread_exit(0 ); return 0 ; }
验证线程的退出方式 3种退出方式,查看退出进程的返回值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include <pthread.h> #include <string.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> void *thread_fun (void *arg) { if (!strcmp ("1" , (char *)arg)) { printf ("new thread return \n" ); return (void *)1 ; }else if (!strcmp ("2" , (char *)arg)) { printf ("new thread pthread_exit \n" ); pthread_exit((void *)2 ); }else { printf ("new thread exit\n" ); exit (3 ); } }int main (int argc, char **argv) { int err; pthread_t tid; if (argc < 2 ) { printf ("input arg\n" ); return 0 ; } err = pthread_create(&tid, NULL , thread_fun, argv[1 ]); if (err != 0 ) { printf ("create new thread failed\n" ); return 0 ; } sleep(1 ); printf ("main thread\n" ); return 0 ; }
通过运行时捕获线程状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> void *thread_fun_1 () { printf ("I'm thread 1\n" ); return (void *)1 ; }void * thread_fun_2 () { printf ("I'm thread 2\n" ); pthread_exit((void *)2 ); return (void *)2 ; }int main (void ) { int err_1, err_2; pthread_t tid_1, tid_2; void *rval_1, *rval_2; err_1 = pthread_create(&tid_1, NULL , thread_fun_1, NULL ); err_2 = pthread_create(&tid_2, NULL , thread_fun_2, NULL ); if (err_1 || err_2) { printf ("create new thread failed!\n" ); return 0 ; } printf ("I'm main thread\n" ); printf ("join1 rval is %d\n" , pthread_join(tid_1, &rval_1)); printf ("join2 rval is %d\n" , pthread_join(tid_2, &rval_2)); printf ("thread 1 exit code is %d\n" , rval_1); printf ("thread 2 exit code is %d\n" , rval_2); printf ("I'm main thread\n" ); }
信号量 信号量#include <semaphore.h>
采用信号量实现同步机制
1 int sem_init(sem_t *sem, int pshared, unsigned int val);
成功返回0, 失败返回EOF sem: 为指定要初始化的信号两对象 pshared:为0时代表线程之间通信, 1代表进程间通信 val 信号量初值
信号量 P / V 操作
1 2 int sem_wait(sem_t *sem); //P操作, 申请资源, 可能会发生阻塞 int sem_post(sem_t *sem); //V操作, 释放资源, 不会发生阻塞
成功时返回0, 失败时返回EOF sem指向要操作的信号量对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <semaphore.h> #include <pthread.h> #include <stdio.h> #include <errno.h> #include <string.h> char buf[512 ];sem_t sem;void *thread_fun (void *arg) { while (1 ) { sem_wait(&sem); printf ("Your input: %s\n" , buf); } }int main (void ) { pthread_t tid; if (sem_init(&sem, 0 , 0 ) != 0 ) { perror("sem_init(): " ); return -1 ; } if (pthread_create(&tid, NULL , thread_fun, NULL ) != 0 ) { perror("pthread_create(): " ); return -1 ; } printf ("Input something, 'quit' to exit program\n" ); do { fgets(buf, 512 , stdin ); sem_post(&sem); }while (strncmp (buf, "quit" , 4 ) != 0 ); return 0 ; }
严格实现同步 分别定义读和写的信号量来实现各种操作的严格同步。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include <semaphore.h> #include <pthread.h> #include <stdio.h> #include <errno.h> #include <string.h> char buf[512 ];sem_t sem_r, sem_w;void *thread_fun (void *arg) { while (1 ) { sem_wait(&sem_r); printf ("Your input: %s\n" , buf); sem_post(&sem_w); } }int main (void ) { pthread_t tid; if (sem_init(&sem_r, 0 , 0 ) != 0 ) { perror("sem_init(): " ); return -1 ; } if (sem_init(&sem_w, 0 , 0 ) != 0 ) { perror("sem_init(): " ); return -1 ; } if (pthread_create(&tid, NULL , thread_fun, NULL ) != 0 ) { perror("pthread_create(): " ); return -1 ; } printf ("Input something, 'quit' to exit program\n" ); do { fgets(buf, 512 , stdin ); sem_post(&sem_r); sem_wait(&sem_w); }while (strncmp (buf, "quit" , 4 ) != 0 ); return 0 ; }