Linux 操作系统学习之线程
Linux在用fork创建进程时,为子进程申请了堆栈、BSS区、全局变量、和代码段等资源。而用pthread_create创建线程时,用户空间分配的资源比线程少得多。可以说,线程就是轻量级的进程。
1.函数 pthread_create()用来创建一个新的线程。其函数声明如下:
1 /*come from /usr/include/pthread.h*/ 2 extern int pthread_create (pthread_t *__restrict __newthread, 3 __const pthread_attr_t *__restrict __attr, 4 void *(*__start_routine) (void *), 5 void *__restrict __arg) __THROWNL __nonnull ((1, 3));
第一个参数用来存储线程的ID,参数为指向线程ID的指针。创建成功,返回新线程ID;_restrict 是C99定义的新标准关键字,主要用来提高编译效率。
第二个参数用来设置线程属性,主要设置与栈相关的属性。一般情况下,此参数设置为NULL,新的线程将使用系统默认的属性。
第三个参数是线程运行的代码起始地址。
第四个参数是运行函数的参数地址,如果需要传入多个参数,需要使用结构体。
示例:
1 #include <pthread.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <string.h> 5 #include <unistd.h> 6 #include <sys/syscall.h> 7 8 struct message 9 { 10 int i; 11 int j; 12 }; 13 14 void *hello (struct message *str); 15 16 int main(int argc, char* argv[]) 17 { 18 struct message test; 19 pthread_t thread_id; 20 test.i = 10; 21 test.j = 20; 22 /*create thread*/ 23 pthread_create(&thread_id, NULL, (void *) *hello, &test); 24 printf("parent, the tid = %lu, pid = %ld\n",pthread_self(),syscall(SYS_gettid)); 25 pthread_join(thread_id,NULL); 26 sleep(1); 27 return 0; 28 } 29 30 void *hello (struct message *str) 31 { 32 printf("child, the tid = %lu, pid = %ld\n",pthread_self(),syscall(SYS_gettid)); 33 printf("the arg.i is %d, arg.j is %d\n",str->i, str->j); 34 }
2.线程的退出和等待:
线程的退出函数声明如下:
extern void pthread_exit (void *__retval) __attribute__ ((__noreturn__));
此函数只有一个参数,即线程退出状态。
等待线程:
为了同步线程,一般主线程都会等待子线程结束,显式的等待某线程结束。可以调用pthread_join实现。其函数声明如下:
extern int pthread_join (pthread_t __th, void **__thread_return);
第一个参数是被等待的线程的ID,第二个参数为用户定义的指针,指向一个保存等待线程的完整退出状态的静态区域,它可以用来存储被等待线程的返回值。
线程的独立:
如果要设置某个线程为独立线程,则可以调用pthread_detach实现。声明如下:
extern int pthread_detach (pthread_t __th)
示例代码:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <pthread.h> 4 #include <string.h> 5 #include <unistd.h> 6 #include <sys/syscall.h> 7 8 void *helloworld(char* arg); 9 10 int main(int argc, char* argv[]) 11 { 12 int error; 13 int *temptr; 14 pthread_t thread_id; 15 /*create thread*/ 16 pthread_create(&thread_id,NULL,(void)helloworld,"hello,world"); 17 printf("*p = %u, p = %u\n",(unsigned int)*helloworld,(unsigned int) helloworld); 18 19 /*wait for child thread terminating*/ 20 pthread_join(thread_id,(void **)&temptr); 21 22 printf("temp = %x, *temp = %c\n",(unsigned)temptr,*temptr); 23 *temptr = 'd'; 24 printf("%c\n",*temptr); 25 free(temptr); 26 return 0; 27 } 28 29 void *helloworld(char *arg) 30 { 31 int *p; 32 p = (int*)malloc(10*sizeof(int)); 33 printf("the message is %s\n",arg); 34 printf("the child id is %u\n",pthread_self()); 35 memset(p,'c',10); 36 printf("p = %x\n",(unsigned)p); 37 /*thread exit*/ 38 pthread_exit(p); 39 }
3.线程退出前的操作
线程的终止会存在资源释放的问题。经常出现的情形:线程为了访问临界资源而为其上锁,但访问过程中该线程被取消,如果线程响应取消,那么临界资源得不到释放。pthread_cleanup_push()/pthread_cleanup_pop()函数用来自动释放资源。其声明如下:
void pthread_cleanup_push(routine, arg) void pthread_cleanup_pop(execute)
当线程退出时,可以先执行线程处理程序。
示例代码:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <pthread.h> 4 #include <unistd.h> 5 6 void cleanup() 7 { 8 printf("clean up\n"); 9 } 10 11 void *test_cancel(void) 12 { 13 pthread_cleanup_push(cleanup,NULL); 14 printf("test_cancel\n"); 15 while(1) 16 { 17 printf("test message\n"); 18 sleep(1); 19 } 20 pthread_cleanup_pop(1); 21 } 22 23 int main() 24 { 25 pthread_t tid; 26 pthread_create(&tid,NULL,(void*)test_cancel,NULL); 27 sleep(4); 28 pthread_cancel(tid); 29 pthread_join(tid,NULL); 30 return 0; 31 }
4.取消线程:
函数声明:
/* Cancel THREAD immediately or at the next possibility. */ extern int pthread_cancel (pthread_t __th);
pthread_cancel 函数请求取消线程。只有当目标线程为可取消状态为PTHREAD_CANCEL_ENABLE时,才可被取消。执行取消操作时,将调用线程的取消清理处理程序(pthread_cleanup_push函数)。而pthread_cancel 的调用者不会等待目标线程操作完成。
设置可取消状态:
pthread_setcancelstate()和pthread_setcanceltype()可以用来设置和查询当前线程的可取消性状态或类型。函数声明分别如下:
1 /* Set cancelability state of current thread to STATE, returning old 2 state in *OLDSTATE if OLDSTATE is not NULL. */ 3 extern int pthread_setcancelstate (int __state, int *__oldstate); 4 5 /* Set cancellation state of current thread to TYPE, returning the old 6 type in *OLDTYPE if OLDTYPE is not NULL. */ 7 extern int pthread_setcanceltype (int __type, int *__oldtype);
对于pthread_setcancelstate,该函数有两个参数,state是要设置的新的状态;oldstate用来存储原来的状态。state的值有:
PTHREAD_CANCEL_DISABLE,除非该线程修改自己的状态,否则不可被取消
PTHREAD_CANCEL_ENABLE,这是默认值。
设置取消类型:
有两个参数:
PTHREAD_CANCEL_ASYNCHRONOUS:可随时执行取消请求。
PTHREAD_CANCEL_DEFFERED: 在取消点取消。
线程取消的示例:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 #include <pthread.h> 5 6 void *thread_function(void *arg); 7 8 int main(int argc, char* argv[]) 9 { 10 //int res; 11 pthread_t a_thread; 12 void *thread_result; 13 pthread_create(&a_thread,NULL,thread_function,NULL); 14 sleep(1); 15 printf("cancelling thread...\n"); 16 pthread_cancel(a_thread); 17 printf("wait for thread to finish...\n"); 18 pthread_join(a_thread,&thread_result); 19 return 0; 20 } 21 22 void *thread_function(void *arg) 23 { 24 int i; 25 //sleep(1); 26 /*set cancel state as disable*/ 27 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL); 28 sleep(3); 29 printf("thread cancel type is disable cannot cancel this thread\n"); 30 31 for(i = 0;i<3;i++) 32 { 33 printf("thread is running (%d)...\n",i); 34 sleep(1); 35 } 36 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL); 37 printf("Now the cancel state is enable\n"); 38 sleep(200); 39 pthread_exit(0); 40 }
5.线程与私有数据
5.1
在多线程程序中,全局变量为所有线程所共享。但有时有必要提供线程私有的全局变量。实现的方法是使用同名却不同内存地址的线程数据结构。这样的数据结构可以由Posix线程库维护,成为线程私有数据TSD(Thread-specific Data)。
创建、注销线程私有数据:
extern int pthread_key_create (pthread_key_t *__key, void (*__destr_function) (void *))
该函数从TSD池中分配一项,将其地址赋值给key供以后使用。如果第二个参数不是空,在线程退出时将以key所提供的数据作为参数调用其指向的资源释放函数,以释放分配的缓冲区。
用pthread_key_create()创建的key都是所有线程可以访问的,但是各个线程可以根据自己的需要修改key,而不影响其他线程的key值。
注销一个TSD:
extern int pthread_key_delete (pthread_key_t __key)
读写线程的私有数据:
extern int pthread_setspecific (pthread_key_t __key, __const void *__pointer) extern void *pthread_getspecific (pthread_key_t __key)
pthread_setspecific将pointer的值与key关联。
pthread_getspecific 将key与相关连的数据读出,数据类型设为void*因此可以指向任何类型。
应用示例:
全局变量,数据共享
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 #include <pthread.h> 5 #include <sys/types.h> 6 7 int key = 100; 8 9 void *helloworld_one(char* arg); 10 void *helloworld_two(char* arg); 11 12 int main(int argc, char* argv[]) 13 { 14 15 pthread_t thread_id_one; 16 pthread_t thread_id_two; 17 pthread_create(&thread_id_one,NULL,(void*)helloworld_one,"hello,world one"); 18 pthread_create(&thread_id_two,NULL,(void*)helloworld_two,"hello,world two"); 19 pthread_join(thread_id_one,NULL); 20 pthread_join(thread_id_two,NULL); 21 return 0; 22 } 23 24 void *helloworld_one(char *arg) 25 { 26 printf("this is thread one, the arg is %s\n",arg); 27 printf("the key is %d\n",key); 28 key++; 29 printf("now the key++ has been excuted\n"); 30 pthread_exit(0); 31 } 32 void *helloworld_two(char *arg) 33 { 34 sleep(1); 35 printf("this is thread two, the arg is %s\n",arg); 36 printf("the key is %d\n",key); 37 pthread_exit(0); 38 }
全局变量,私有数据
1 #include <stdio.h> 2 #include <pthread.h> 3 #include <stdlib.h> 4 #include <unistd.h> 5 6 pthread_key_t key;/*线程私有数据类型*/ 7 8 void echomsg(void *t) 9 { 10 printf("destructor excuted in thread %u, param = %u\n",pthread_self(),((int*)t)); 11 } 12 void *child_one(void *arg) 13 { 14 int i = 10; 15 int tid = pthread_self(); 16 printf("set key value %d in thread %u\n",i,tid); 17 /*modify the value of key*/ 18 pthread_setspecific(key,&i); 19 /*等待线程二修改值*/ 20 printf("thread one sleep 2 until thread two finish\n"); 21 sleep(2); 22 /*print the current value of the thread*/ 23 printf("thread %u return %d, add is %u\n",tid, *((int*)pthread_getspecific(key)),(int*)pthread_getspecific(key)); 24 } 25 void *child_two(void *arg) 26 { 27 int i = 20; 28 int tid = pthread_self(); 29 printf("set key value %d in thread %u\n",i,tid); 30 /*modify the value of key*/ 31 pthread_setspecific(key,&i); 32 printf("thread two sleep 1 \n"); 33 sleep(1); 34 /*print the current value of the thread*/ 35 printf("thread %u return %d, add is %u\n",tid, *((int*)pthread_getspecific(key)),(int*)pthread_getspecific(key)); 36 } 37 int main(void) 38 { 39 pthread_t tid1,tid2; 40 pthread_key_create(&key,echomsg); 41 pthread_create(&tid1,NULL,(void*)child_one,NULL); 42 pthread_create(&tid2,NULL,(void*)child_two,NULL); 43 pthread_join(tid1,NULL); 44 pthread_join(tid2,NULL); 45 pthread_key_delete(key); 46 return 0; 47 }