目录
第1关:线程操作实战1
任务描述
本关任务:编写一个小程序取消已生成线程,然后输出入栈信息。
相关知识
为了完成本关任务,你需要掌握:线程栈的相关知识。
线程的退出等待
线程退出等待 1)pthread_exit(void* _retval) 库函数调用可以结束一个线程,结束方式和进程exit()类似。
2)等待线程 为了有效同步子线程,主线程中豆浆等待子线程结束,显示等待某线程结束可以调用pthread_join()函数,类似于进程的wait(),声明如下:
int pthread_join(pthread_t _th, void **_pthread_return)
_th 为被等待线程的ID(设置某个线程为独立线程,则可以调用pthread_detach(pthread_t __th))
_pthread_return 为一个用户定义的指针,指向一个保存等待线程的完整退出状态的静态区域,用来存储被等待线程的返回值
阻塞调用当前线程的线程,直到此线程退出。当函数返回,处于被等待状态的线程资源被收回。
编程要求
根据提示,在右侧编辑器编写代码,增强对pthread_cleanup_push,pthread_cleanup_pop等函数的使用。
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
void cleanup()
{
printf("cleanup\n");
}
void *test_cancel(void)
{
/*补充代码1,使用pthread_cleanup_push/pop,进行退出前操作*/
pthread_cleanup_push(cleanup,NULL);
printf("test_cancel\n");
while(1)
{
printf("test message\n");
sleep(1);
}
/*补充代码2,使用pthread_cleanup_push/pop,进行退出前操作*/
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);
}
第2关:线程操作实战2
任务描述
本关任务:编写一个小程序实现不同状态下的取消线程设置。
相关知识
为了完成本关任务,你需要掌握:取消线程的相关操作。
取消线程
(1)发起取消 即取消正在执行线程的操作,需要满足条件。 pthread_cancel()取消线程,调用的是取消线程清理处理程序(pthread_cleanup_push) 1)线程是否可以被取消,默认可以取消。 2)线程处于可取消点才能取消,发起取消不一定能立即取消,到取消点才可以。 (2)设置可取消状态 pthread_setcancelstate()和pthread_setcanceltype()用来设置和查询当前线程的可取消性状态或类型。
pthread_setcancelstate(int _state,int *_oldstate)
state为要设置的新状态值;
oldstate存储原来状态。
1)PTHREAD_CANCEL_DISABLE 针对目标线程的取消请求将处于未决状态(未处理但存在)除非线程修改自己状态,否则不会被取消。
2)PTHREAD_CANCEL_ENABLE,针对目标线程的取消请求将被传递,创建线程时是默认状态
(3)设置取消类型 pthread_setcanceltype()设置取消类型,在允许被取消的线程在接收到取消操作后是立即终止还是等到取消点终止。
int pthread_setcanceltype(int _type, int *_oldtype)
type调用线程新的可取消性;
oldtype存储原来的类型。
PTHREAD_CANCEL_ASYNCHRONOUS,可随时执行新的或未决的取消请求。
PTHREAD_CANCEL_DEFERRED,目标达到取消点前处于未决状态(请求已发出但未处理)
在创建线程时,可取消类型设置PTHREAD_CANCEL_DEFERRED,如果禁用线程的可取消性状态,则可取消性类型设置不会立即生效,取消请求都保留为未决状态。但一旦重新启用可取消性,新类型将会生效。 成功完成pthread_setcancelstate和pthread_setcanceltype将返回0,失败返回错误编号,不许设置error变量。
编程要求
根据提示,在右侧编辑器补充代码,代码思路是先开一个线程a_thread,调用thread_function函数,线程函数中先休眠1秒,然后输出一段提示,主函数休眠10秒,a_thread线程开始跑。function函数中,先将线程的可取消状态设置成DISABLE,当前无法取消线程,等待3秒输出当前线程无法取消,然后循环输出三次,每秒一次。将取消状态设置成ENABLE,可取消,线程休眠200秒,返回主程序,输出取消线程,主程序将线程取消pthread_cancel ,输出等待线程完成,pthread_join等待子线程结束,释放所有缓存。请补充代码,分别将取消状态设置为DISABLE和ENABLE。
#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);
}
printf("Cancelling thread...\n");
sleep(10);
res = pthread_cancel(a_thread);//取消子线程
if (res != 0)
{
perror("Thread cancelation failed");
exit(EXIT_FAILURE);
}
printf("Waiting for thread to finish...\n");
sleep(10);
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);
/*请编写代码,将线程的取消状态设置为DISABLE*/
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\n");//打印不可取消状态信息
for(i = 0; i <3; i++)
{
printf("Thread is running (%d)...\n", i);
sleep(1);
}
/*请编写代码,将取消状态设置为ENABLE*/
res = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
if (res != 0)
{
perror("Thread pthread_setcancelstate failed");
exit(EXIT_FAILURE);
}
else
printf("Now change ths canclestate is ENABLE\n");
sleep(7);//休眠7秒
pthread_exit(0);
}
第3关:线程操作实战3
任务描述
本关任务:编写一个小程序修改线程的私有变量查看对全局变量的影响。
相关知识
为了完成本关任务,你需要掌握并理解:线程的全局数据与私有数据。
线程全局变量与私有变量
经常用全局变量以实现多个函数共享数据,由于数据空间共享,全局变量共有,有的需要提供线程私有的全局变量,例如每个线程维护一个链表,使用相同函数操作,使用同名而不同内存地址的线程私有数据结构,这样的数据结构可以由POSIX线程库维护,称为线程私有数据TSD (1)创建、注销线程私有数据 ①创建TSD pthread_key_create(pthread_ket_t *key,2)
从TSD池中分配一项,将其地址值赋给key,第二个参数不为空,在线程退出(pthread_exit())时以key所关联的数据为参数调用其指向的资源释放函数,以释放分配的缓冲区。 不论哪个线程调用pthread_key_create(),创建的key所有线程都可以访问,每个线程根据需要往key填入不同的值,提供了一个同名不同值的全局变量。
②注销TSD int pthread_key_delete(pthread_key_t key)
这个函数不检查当前是否有线程正在使用TSD,也不会调用清理函数,只是将TSD释放供下一次调用pthread_key_create 使用。
(2)读写线程私有数据 TSD的读写通过专门的POSIX Thread函数进行
int pthread_setspecific(pthread_key_t key,const coid *pointer)
void *pthread_getspecific(pthread_key_t key)
函数pthread_setspcific()将pointer的值(不是所指内容)与key相关联,pthread_getspecific()函数将与key相关联的数据读出。
先设置一个全局变量k=100,在主函数中先创建两个线程,线程1和2都输出线程赋值的字符串helloworld,线程2休眠1秒让线程1先跑,线程1将全局变量key改成10,输出key值和当前线程号,线程1跑完返回主线程pthread_join等待结束,线程2休眠完开跑,输出key,因为key值是全局变量,当前key已经被线程1修改,然后返回结束。
编程要求
根据提示,在右侧编辑器补充代码,代码思路是先创建两个线程child1、child2,两个线程同时开跑,线程1和2都输出当前欲修改的值以及自身的线程标识,然后分别用setspecific修改key值,为10和20,分别等待1秒和2秒,并打印当前的全局变量值,线程2先打印,线程1后打印,线程1和2分别都对私有的全局变量进行改变,但互不影响。请补充代码,分别将线程1的私有全局变量值修改成10,线程2 的私有全局变量值修改成20。
#include <stdio.h>
#include <pthread.h>
pthread_key_t key; //线程私有数据类型
void echomsg(void *t)
{
printf("destructor excuted\n"); //线程退出时执行
}
void * child1(void *arg)
{
int i=10;
int tid=pthread_self();
printf("\nset key value %d in thread 1\n",i);
/*将线程1的私有key值修改为10*/
pthread_setspecific(key, (void*)&i);
printf("thread one sleep 2 until thread two finish\n");
sleep(3); //等待线程2修改key值
printf("\nthread 1 returns %d\n",*((int *)pthread_getspecific(key))); //打印当前线程信息
}
void * child2(void *arg)
{
int temp=20;
int tid=pthread_self();
sleep(1);
printf("\nset key value %d in thread 2\n\n",temp);
/*将线程2的私有key值修改为20*/
pthread_setspecific(key, (void*)&temp);
sleep(1);
printf("thread 2 returns %d\n",*((int *)pthread_getspecific(key))); //打印当前线程信息
}
int main(void)
{
pthread_t tid1,tid2;
pthread_key_create(&key,echomsg);
pthread_create(&tid1,NULL,(void *)child1,NULL); //创建线程1
pthread_create(&tid2,NULL,(void *)child2,NULL); //创建线程2
pthread_join(tid1,NULL);//等待线程1结束
pthread_join(tid2,NULL);//等待线程2结束
pthread_key_delete(key); //释放全局变量
return 0;
}