线程与进程对比
1.用户空间对比
2.内核空间资源对比
在创建线程时,Linux内核仍然创建一个新的PCB来标识这个线程。内核并不认为进程与线程有差别。
进程是操作系统管理资源的基本单元,线程时Linux系统调度的基本单元。
3.进程线程函数对比
创建线程
int pthread_create (pthread_t *__restrict __newthread,
__const pathread_attr_t *__restrict __attr,
void *(*__start_routine) (void *),
void *__restrict __arg)
成功返回0,失败返回非0值。
#include<pthread.h> #include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<sys/syscall.h> struct message { int i; int j; }; void * hello(struct message *str) { printf("child, the tid = %lu, pid = %ld ", pthread_self(),syscall(SYS_gettid)); printf("the arg.i is %d, arg.j is %d ", str->i, str->j); while(1); } int main(int argc, char *argv[]) { struct message test; pthread_t thread_id; test.i = 10; test.j = 20; pthread_create(&thread_id, NULL, (void *)*hello, &test); //创建线程 printf("parent, the tid = %lu, pid = %ld ", pthread_self(),syscall(SYS_gettid)); pthread_join(thread_id, NULL); }
注意,编译的时候需要加上 -lpthread
gcc -o 名字 源文件 -lphtread (无法使用perror打印错误信息,因为不修改系统全局变量errno)
void pthread_exit (void *__retval) :线程退出,与exit()函数类似
int pthread_join (pthread_t __th, void **__thread_return) :阻塞调用当前线程的线程,直到此线程退出。类似于wait函数。
第一个参数:被等待线程ID。必须等待关联线程
第二个参数:用户定义指针,存储被等待线程返回值。
int pthread_detach (pthread_t __th) :设置线程为独立线程。成功返回0.
测试线程退出时全局变量和堆变量:仍然可用
#include<pthread.h> #include<stdio.h> #include<stdlib.h> #include<string.h> void *helloworld(char *argc); int main(int argc, char *argv[]) { int error; int *temptr; pthread_t thread_id; pthread_create(&thread_id, NULL, (void *)*helloworld, "helloworld"); //测试下面两者是否有区别 printf("*p = %x, p = %x ", *helloworld, helloworld); if(error = pthread_join(thread_id, (void**)&temptr)) { perror("pthread_join"); exit(EXIT_FAILURE); } //打印子线程退出时的值 printf("temp = %x, *temp = %c ", temptr, *temptr); //修改堆空间 测试是否可用 *temptr = 'd'; printf("%c ", *temptr); free(temptr); return 0; } void *helloworld(char *argc) { int *p; p = (int *)malloc(10 * sizeof(int)); printf("the message is %s ", argc); printf("the child id is %u ", pthread_self()); memset(p, 'c', 10); printf("p = %x ", p); pthread_exit(p); //退出线程,堆空间首地址做为返回信息 }
可以看到,函数名 helloworld 和 *helloworld 是一样的。
线程退出时资源释放
void pthread_cleanup_push(void (*routine) (void *), void *arg) :压入清理函数栈,后进先出
void pthread_cleanup_pop(int execute) :参数为0,表示不执行弹出的清理函数;非0执行。
#include<pthread.h> #include<unistd.h> #include<stdlib.h> #include<stdio.h> void cleanup() { printf("cleanup "); } void *test_cancel(void) { pthread_cleanup_push(cleanup, NULL); printf("test_cancel "); while(1) { printf("test message "); sleep(1); } pthread_cleanup_pop(1); } int main() { pthread_t tid; //创建线程 pthread_create(&tid, NULL, (void *)test_cancel, NULL); sleep(2); //取消子线程 pthread_cancel(tid); pthread_join(tid, NULL); }
取消线程
条件1:线程必须可以被其他线程取消,默认可以
条件2:线程处于可取消点才能被取消。
int pthread_cancel (pthread_t __cancelthread) :向某线程发送取消操作。
int pthread_setcancelstate (int __state, int *__oldstate) :设置当前线程的可取消性, state为新状况,oldstate存储原来的状态。
PTHREAD_CANCEL_DISABLE为不可取消;
PTHREAD_CANCEL_ENABLE为可取消。(默认值)
int pthread_setcanceltype (int __type, int *__oldtype) :设置取消类型。
PTHREAD_CANCEL_ASYNCHRONOUS :可随时执行新的或未决的取消请求
PTHREAD_CANCEL_DEFERRED :在目标线程到达取消点之前,取消请求处于未决状态。(默认值)
#include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<pthread.h> void *thread_function(void *arg); int main(int argc, char *argv[]) { int res; pthread_t a_thread; void *thread_result; //创建线程 res = pthread_create(&a_thread, NULL, thread_function, NULL); if(res != 0) { perror("Thread creation failed"); exit(EXIT_FAILURE); } sleep(3); printf("Cancelling thread... "); //取消子线程 res = pthread_cancel(a_thread); if(res != 0) { perror("Thread cancelation failed"); exit(EXIT_FAILURE); } printf("Waitint for thread to finish... "); //等待子线程结束 res = pthread_join(a_thread, &thread_result); if(res != 0) { perror("Thread join failed"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } //新线程执行函数 void *thread_function(void *arg) { int i, res, j; sleep(1); //设置为不可取消 res = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); if(res != 0) { perror("Thread pthread_setcancelstate failed"); exit(EXIT_FAILURE); } sleep(3); printf("thread cancle type is disable, can't cancle this thread "); for(i = 0; i < 3; i++) { printf("Thread is running (%d)... ", i); sleep(1); } //设置为可取消 res = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); if(res != 0) { perror("Thread pthread_setcancelstate failed"); exit(EXIT_FAILURE); } printf("Now change the canclestate is ENABLE "); sleep(200); pthread_exit(0); }
线程与私有数据
如果需要每个线程有自己私有的全局变量,可以使用同名而不同内存地址的线程私有数据结构,成为私有数据TSD。
int pthread_key_create(pthread_key_t *key, void (*destr_function)(void *)) :创建线程私有数据,地址赋值给key. 所有线程对key可见,但线程可以根据自己的需要,在key中填入不同值,相当于同名不同值。第二个参数表示线程退出时key的资源释放函数。
int pthread_key_delete(pthread_key_t key) :注销一个TSD,不调用destr_sunction?
int pthread_setspecific (pthread_key_t key, const void *pointer) :将pointer的值与key相关联。
void * pthread_getspecific (pthread_key_t key) :读取与key相关联的数据
全局变量测试,不使用TSD的情况。
#include<stdio.h> #include<pthread.h> #include<unistd.h> #include<stdlib.h> //全局变量,初值100 int key = 100; void *helloworld_one(char * argc) { printf("the message is %s ", argc); //修改值为10 key = 10; printf("key=%d, the child id is %u ", key, pthread_self()); return 0; } void *helloworld_two(char * argc) { printf("the message is %s ", argc); //休眠,让另一个线程先修改值 sleep(1); printf("key=%d, the child id is %u ", key, pthread_self()); return 0; } int main() { pthread_t thread_id_one; pthread_t thread_id_two; pthread_create(&thread_id_one, NULL, (void *)*helloworld_one, "helloworld"); pthread_create(&thread_id_two, NULL, (void *)*helloworld_two, "helloworld"); pthread_join(thread_id_one, NULL); pthread_join(thread_id_two, NULL); }
两个线程打印的都是修改后的值
使用TSD,值不同
#include<stdio.h> #include<pthread.h> //线程私有数据类型 pthread_key_t key; void echomsg(void *t) { printf("destructor excuted in thread %u, param=%u ",pthread_self(),((int *)t)); } void * child1(void *arg) { int i = 10; int tid = pthread_self(); printf(" set key value %d in thread %u ", i, tid); //修改私有数据值 pthread_setspecific(key, &i); //等待让另一个线程修改值 printf("thread one sleep 2 until thread two finish "); sleep(2); printf(" thread %u returns %d, add is %u ", tid, *((int *)pthread_getspecific(key)), (int *)pthread_getspecific(key)); } void * child2(void *arg) { int temp = 20; int tid = pthread_self(); printf(" set key value %d in thread %u ", temp, tid); //修改私有数据值 pthread_setspecific(key, &temp); sleep(1); printf(" thread %u returns %d, add is %u ", tid, *((int *)pthread_getspecific(key)), (int *)pthread_getspecific(key)); } int main(void) { pthread_t tid1, tid2; pthread_key_create(&key, echomsg); pthread_create(&tid1, NULL, (void *)child1, NULL); pthread_create(&tid2, NULL, (void *)child2, NULL); pthread_join(tid1, NULL); pthread_join(tid2, NULL); pthread_key_delete(key); return 0; }