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 }
View Code

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 }
View Code

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 }
View Code

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 }
View Code

全局变量,私有数据

 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 }
View Code

 

 

 

posted @ 2013-12-10 14:13  永久指针  阅读(412)  评论(0编辑  收藏  举报