如何正确的终止正在运行的子线程
首先我们来看一下,让当前正在运行的子线程停止的所有方法
1.任何一个线程调用exit
2.pthread_exit
3.pthread_kill
4.pthread_cancel
下面我们一一分析各种终止正在运行的程序的方法
任何一个线程调用exit
任何一个线程只要调用了exit都会导致进程结束,各种子线程当然也能很好的结束了,可是这种退出会有一个资源释放的问题.我们知道当一个进程终止时,内核对该进程所有尚未关闭的文件描述符调用close关闭,所以即使用户程序不调用close,在终止时内核也会自动关闭它打开的所有文件。没错,标准C++ IO流也会很好的在exit退出时得到flush并且释放资源,这些东西并不会造成资源的浪费(系统调用main函数入口类似于exit(main(argc,argv))).表面上似乎所有的问题都能随着进程的结束来得到很好的处理,其实并不然,我们程序从堆上分配的内存就不能得到很好的释放,如new ,delete后的存储空间,这些空间进程结束并不会帮你把这部分内存归还给内存.(本文初稿时,因基础不牢固,此处写错,事实上无论进程这样结束,系统都将会释放掉所有代码所申请的资源,无论是堆上的还是栈上的。(感谢ZKey的指导)。这种结束所有线程(包括主线程)的方式实际上在很多时候是非常可取的,但是对于针对关闭时进行一些别的逻辑的处理(指非资源释放逻辑)就不会很好,例如我想在程序被kill掉之前统计一下完成了多少的工作,这个统计类似于MapReduce,需要去每个线程获取,并且最后归并程一个统一的结果等等场景)
pthread_exit
此函数的使用场景是当前运行的线程运行pthread_exit得到退出,对于各个子线程能够清楚地知道自己在什么时候结束的情景下,非常好用,可是实际上往往很多时候一个线程不能知道知道在什么时候该结束,例如遭遇Ctrl+C时,kill进程时,当然如果排除所有的外界干扰的话,那就让每个线程干完自己的事情后,然后自觉地乖乖的调用pthread_exit就可以了,这并不是本文需要讨论的内容,本文的情景就是讨论如何处理特殊情况。
这里还有一种方法,既然子线程可以通过pthread_exit来正确退出,那么我们可以在遭遇Ctrl+C时,kill进程时处理signal信号,然后分别给在某一个线程可以访问的公共区域存上一个flag变量,线程内部每运行一段时间(很短)来检查一下flag,若发现需要终止自己时,自己调用pthread_exit,此法有一个弱点就是当子线程需要进行阻塞的操作时,可能无暇顾及检查flag,例如socket阻塞操作。如果你的子线程的任务基本没有非阻塞的函数,那么这么干也不失为一种很好的方案。
pthread_kill
不要被这个可怕的邪恶的名字所吓倒,其实pthread_kill并不像他的名字那样威力大,使用之后,你会感觉,他徒有虚名而已
pthread_kill的职责其实只是向指定的线程发送signal信号而已,并没有真正的kill掉一个线程,当然这里需要说明一下,有些信号的默认行为就是exit,那此时你使用pthread_kill发送信号给目标线程,目标线程会根据这个信号的默认行为进行操作,有可能是exit。当然我们同时也可以更改获取某个信号的行为,以此来达到我们终止子线程的目的。
1 #define _MULTI_THREADED
2 #include
3 #include
4 #include
5 #include "check.h"
6
7 #define NUMTHREADS 3
8 void sighand(int signo);
9
10 void *threadfunc(void *parm)
11 {
12 pthread_t self = pthread_self();
13 pthread_id_np_t tid;
14 int rc;
15
16 pthread_getunique_np(&self, &tid);
17 printf("Thread 0x%.8x %.8x entered\n", tid);
18 errno = 0;
19 rc = sleep(30);
20 if (rc != 0 && errno == EINTR) {
21 printf("Thread 0x%.8x %.8x got a signal delivered to it\n",
22 tid);
23 return NULL;
24 }
25 printf("Thread 0x%.8x %.8x did not get expected results! rc=%d, errno=%d\n",
26 tid, rc, errno);
27 return NULL;
28 }
29
30 int main(int argc, char **argv)
31 {
32 int rc;
33 int i;
34 struct sigaction actions;
35 pthread_t threads[NUMTHREADS];
36
37 printf("Enter Testcase - %s\n", argv[0]);
38
39 printf("Set up the alarm handler for the process\n");
40 memset(&actions, 0, sizeof(actions));
41 sigemptyset(&actions.sa_mask);
42 actions.sa_flags = 0;
43 actions.sa_handler = sighand;
44
45 rc = sigaction(SIGALRM,&actions,NULL);
46 checkResults("sigaction\n", rc);
47
48 for(i=0; i<NUMTHREADS; ++i) {
49 rc = pthread_create(&threads[i], NULL, threadfunc, NULL);
50 checkResults("pthread_create()\n", rc);
51 }
52
53 sleep(3);
54 for(i=0; i<NUMTHREADS; ++i) {
55 rc = pthread_kill(threads[i], SIGALRM);
56 checkResults("pthread_kill()\n", rc);
57 }
58
59 for(i=0; i<NUMTHREADS; ++i) {
60 rc = pthread_join(threads[i], NULL);
61 checkResults("pthread_join()\n", rc);
62 }
63 printf("Main completed\n");
64 return 0;
65 }
66
67 void sighand(int signo)
68 {
69 pthread_t self = pthread_self();
70 pthread_id_np_t tid;
71
72 pthread_getunique_np(&self, &tid);
73 printf("Thread 0x%.8x %.8x in signal handler\n",
74 tid);
75 return;
76 }
运行输出为:
1 Output:
2
3 Enter Testcase - QP0WTEST/TPKILL0
4 Set up the alarm handler for the process
5 Thread 0x00000000 0000000c entered
6 Thread 0x00000000 0000000d entered
7 Thread 0x00000000 0000000e entered
8 Thread 0x00000000 0000000c in signal handler
9 Thread 0x00000000 0000000c got a signal delivered to it
10 Thread 0x00000000 0000000d in signal handler
11 Thread 0x00000000 0000000d got a signal delivered to it
12 Thread 0x00000000 0000000e in signal handler
13 Thread 0x00000000 0000000e got a signal delivered to it
14 Main completed
我们可以通过截获的signal信号,来释放掉线程申请的资源,可是遗憾的是我们不能再signal处理里调用pthread_exit来终结掉线程,因为pthread_exit是中介当前线程,而signal被调用的方式可以理解为内核的回调,不是在同一个线程运行的,所以这里只能做处理释放资源的事情,线程内部只有判断有没有被中断(一般是EINTR)来断定是否要求自己结束,判定后可以调用pthread_exit退出。
此法对于一般的操作也是非常可行的,可是在有的情况下就不是一个比较好的方法了,比如我们有一些线程在处理网络IO事件,假设它是一种一个客户端对应一个服务器线程,阻塞从Socket中读消息的情况。我们一般在网络IO的库里面回家上对EINTR信号的处理,例如recv时发现返回值小于0,检查error后,会进行他对应的操作。有可能他会再recv一次,那就相当于我的线程根本就不回终止,因为网络IO的类有可能不知道在获取EINTR时要终止线程。也就是说这不是一个特别好的可移植方案,如果你线程里的操作使用了很多外来的不太熟悉的类,而且你并不是他对EINTR的处理手段是什么,这是你在使用这样的方法来终止就有可能出问题了。而且如果你不是特别熟悉这方面的话你会很苦恼,“为什么我的测试代码全是ok的,一加入你们部门开发的框架进来就不ok了,肯定是你们框架出问题了”。好了,为了不必要的麻烦,我最后没有使用这个方案。
pthread_cancel
这个方案是我最终采用的方案,我认为是解决这个问题,通用的最好的解决方案,虽然前面其他方案的有些问题他可能也不好解决,但是相比较而言,还是相当不错的
pthread_cancel可以单独使用,因为在很多系统函数里面本身就有很多的断点,当调用这些系统函数时就会命中其内部的断点来结束线程,如下面的代码中,即便注释掉我们自己设置的断点pthread_testcancel()程序还是一样的会被成功的cancel掉,因为printf函数内部有取消点(如果大家想了解更多的函数的取消点情况,可以阅读《Unix高级环境编程》的线程部分)
1 #include
2 #include
3 #include
4 #include
5 void *threadfunc(void *parm)
6 {
7 printf("Entered secondary thread\n");
8 while (1) {
9 printf("Secondary thread is looping\n");
10 pthread_testcancel();
11 sleep(1);
12 }
13 return NULL;
14 }
15
16 int main(int argc, char **argv)
17 {
18 pthread_t thread;
19 int rc=0;
20
21 printf("Entering testcase\n");
22
23 /* Create a thread using default attributes */
24 printf("Create thread using the NULL attributes\n");
25 rc = pthread_create(&thread, NULL, threadfunc, NULL);
26 checkResults("pthread_create(NULL)\n", rc);
27
28 /* sleep() is not a very robust way to wait for the thread */
29 sleep(1);
30
31 printf("Cancel the thread\n");
32 rc = pthread_cancel(thread);
33 checkResults("pthread_cancel()\n", rc);
34
35 /* sleep() is not a very robust way to wait for the thread */
36 sleep(10);
37 printf("Main completed\n");
38 return 0;
39 }
输出:
Entering testcase
Create thread using the NULL attributes
Entered secondary thread
Secondary thread is looping
Cancel the thread
Main completed
POSIX保证了绝大部分的系统调用函数内部有取消点,我们看到很多在cancel调用的情景下,recv和send函数最后都会设置pthread_testcancel()取消点,其实这不是那么有必要的,那么究竟什么时候该pthread_testcancel()出场呢?《Unix高级环境编程》也说了,当遇到大量的基础计算时(如科学计算),需要自己来设置取消点。
ok,得益于pthread_cancel,我们很轻松的把线程可以cancel掉,可是我们的资源呢?何时释放...
下面来看两个pthread函数
1.void pthread_cleanup_push(void (*routine)(void *), void *arg);
2.void pthread_cleanup_pop(int execute);
这两个函数能够保证在 1函数调用之后,2函数调用之前的任何形式的线程结束调用向pthread_cleanup_push注册的回调函数
另外我们还可通过下面这个函数来设置一些状态。
请问各位什么情况下用pthread_setcancelstate()和pthread_testcancel()函数?
你的理解不对。 首先,pthread_setcancelstate()函数只是改变本线程(注意是本线程)的cancel state。所以T1进入fun()函数,执行到pthread_setcancelstate()函数时,只是改变了T1本身的cancel state,并不能改变T2的cancel state。 第二,线程执行到pthread_testcancel()函数时,并不一定会马上取消(退出)。 先描述一下取消一个线程的过程:1) 其他线程通过调用pthread_cancel()函数,向目标线程发送取消请求(cancellation request)。2) 取消请求发出后,根据目标线程的cancel state来决定取消请求是否会到达目标线程:a. 如果目标线程的cancel state是PTHREAD_CANCEL_ENABLE(默认),取消请求会到达目标线程。b. 如果目标线程的cancel state是PTHREAD_CANCEL_DISABLE,取消请求会被放入队列。直到目标线程的cancel state变为PTHREAD_CANCEL_ENABLE,取消请求才会从队列里取出,发到目标线程。3) 取消请求到达目标线程后,根据目标线程的cancel type来决定线程何时取消:a. 如果目标线程的cancel type是PTHREAD_CANCEL_DEFERRED(默认),目标线程并不会马上取消,而是在执行下一条cancellation point的时候才会取消。有很多系统函数都是cancellation point,详细的列表可以在Linux上用man 7 pthreads查看。除了列出来的cancellation point,pthread_testcancel()也是一个cancellation point。就是说目标线程执行到pthread_testcancel()函数的时候,如果该线程收到过取消请求,而且它的cancel type是PTHREAD_CANCEL_DEFERRED,那么这个线程就会在这个函数里取消(退出),这个函数就不再返回了,目标线程也没有了。b. 如果目标线程的cancel type是PTHREAD_CANCEL_ASYNCHRONOUS,目标线程会立即取消(这里的“立即”只是说目标线程不用等执行到属于cancellation point的函数的时候才会取消,它会在获得调度之后立即取消,因为内核调度会有延时,所以并不能保证时间上的“立即”)。 举个例子,说明一下这些与线程取消相关的函数的用法:void thread_function(void *arg){/*** 线程准备执行一些关键工作,在这个过程中不希望被取消。* 所以先通过pthread_setcancelstate()将本线程的cancel state* 设为disabled。*/pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);/* 执行关键工作 */.../*** 关键工作执行完成,可以被取消。* 通过pthread_setcancelstate()将本线程的cancel state* 设为enabled。*/pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);/*** 调用pthread_testcancel()函数,检查一下在cancel state* 为disabled状态的时候,是否有取消请求发送给本线程。* 如果有的话就取消(退出)。*/pthread_testcancel();/*** pthread_testcancel()返回了,表明之前没有取消请求发送给本线程,* 继续其余的工作。* 这时候如果有取消请求发送给本线程,会在下一次执行到* cancellation point的时候(例如sleep(), read(), write(), ...)时取消。*/.../*** 从这里开始,函数里不再包含cancellation point了。* 如果收到取消请求,将无法取消。所以先把本线程的cancel type* 设为asynchronous,收到取消请求将立即取消。*/pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);/* 不包含cancellation point的代码 */...}
Linux 线程同步有哪些方法?
Linux系统中,实现线程同步的方式大致分为六种,其中包括:互斥锁、自旋锁、信号量、条件变量、读写锁、屏障。最常用的线程同步方式就是互斥锁、自旋锁、信号量:1、互斥锁互斥锁本质就是一个特殊的全局变量,拥有lock和unlock两种状态,unlock的互斥锁可以由某个线程获得,当互斥锁由某个线程持有后,这个互斥锁会锁上变成lock状态,此后只有该线程有权力打开该锁,其他想要获得该互斥锁的线程都会阻塞,直到互斥锁被解锁。互斥锁的类型:①普通锁:互斥锁默认类型。当一个线程对一个普通锁加锁以后,其余请求该锁的线程将形成一个等待队列,并在锁解锁后按照优先级获得它,这种锁类型保证了资源分配的公平性。一个线程如果对一个已经加锁的普通锁再次加锁,将引发死锁;对一个已经被其他线程加锁的普通锁解锁,或者对一个已经解锁的普通锁再次解锁,将导致不可预期的后果。②检错锁:一个线程如果对一个已经加锁的检错锁再次加锁,则加锁操作返回EDEADLK;对一个已经被其他线程加锁的检错锁解锁或者对一个已经解锁的检错锁再次解锁,则解锁操作返回EPERM。③嵌套锁:该锁允许一个线程在释放锁之前多次对它加锁而不发生死锁;其他线程要获得这个锁,则当前锁的拥有者必须执行多次解锁操作;对一个已经被其他线程加锁的嵌套锁解锁,或者对一个已经解锁的嵌套锁再次解锁,则解锁操作返回EPERM。④默认锁:一个线程如果对一个已经解锁的默认锁再次加锁,或者对一个已经被其他线程加锁的默认锁解锁,或者对一个解锁的默认锁解锁,将导致不可预期的后果;这种锁实现的时候可能被映射成上述三种锁之一。【老男孩教育】Linux运维云计算课程汇集了虚拟化、云计算、安全攻防、Python开发、SRE等技术,课堂效率高、内容丰富全面,由浅入深,循序渐进,帮助学员稳扎稳打,夯实基础,在有限的时间内帮助学员高效提升,成为符合企业需求的技术型人才。2、自旋锁自旋锁顾名思义就是一个死循环,不停的轮询,当一个线程未获得自旋锁时,不会像互斥锁一样进入阻塞休眠状态,而是不停的轮询获取锁,如果自旋锁能够很快被释放,那么性能就会很高,如果自旋锁长时间不能够被释放,甚至里面还有大量的IO阻塞,就会导致其他获取锁的线程一直空轮询,导致CPU使用率达到100%,特别CPU时间。3、信号量信号量是一个计数器,用于控制访问有限共享资源的线程数。
Linux线程及同步
linux多线程
1.线程概述
线程是一个进程内的基本调度单位,也可以称为轻量级进程。线程是在共享内存空间中并发的多道执行路径,它们共享一个进程的资源,如文件描述和信号处理。因此,大大减少了上下文切换的开销。一个进程可以有多个线程,也就
是有多个线程控制表及堆栈寄存器,但却共享一个用户地址空间。
2.线程实现
线程创建pthread_create()
所需头文件#include
函数原型int
pthread_create
((pthread_t
*thread,
pthread_attr_t
*attr,
thread:线程标识符
attr:线程属性设置
start_routine:线程函数的起始地址
arg:传递给start_routine的参数
函数返回值
成功:0
出错:-1
线程退出pthread_exit();
所需头文件#include
函数原型void
pthread_exit(void
*retval)
函数传入值retval:pthread_exit()调用者线程的返回值,可由其他函数如pthread_join
来检索获取
等待线程退出并释放资源pthread_join()
所需头文件#include
函数原型int
pthread_join
((pthread_t
th,
void
**thread_return))
函数传入值
th:等待线程的标识符
thread_return:用户定义的指针,用来存储被等待线程的返回值(不为NULL时)
函数返回值
成功:0
出错:-1
代码举例
1.
#include
2.
#include
3.
#include
4.
5.
/*线程1*/
6.
void
thread1()
7.
{
8.
int
i=0;
9.
10.
while(1)
11.
{
12.
printf(thread1:%d/n,i);
13.
if(i>3)
14.
pthread_exit(0);
15.
i++;
16.
sleep(1);
17.
}
18.
}
19.
20.
/*线程2*/
21.
void
thread2()
22.
{
23.
int
i=0;
24.
25.
while(1)
26.
{
27.
printf(thread2:%d/n,i);
28.
if(i>5)
29.
pthread_exit(0);
30.
i++;
31.
sleep(1);
32.
}
33.
}
34.
35.
int
main()
36.
{
37.
pthread_t
t1,t2;
38.
39.
/*创建线程*/
40.
pthread_create(&t1,NULL,(void
*)thread1,NULL);
41.
pthread_create(&t2,NULL,(void
*)thread2,NULL);
42.
/*等待线程退出*/
43.
pthread_join(t1,NULL);
44.
pthread_join(t2,NULL);
45.
return
0;
46.
}
3同步与互斥
互斥锁
互斥锁的操作主要包括以下几个步骤。
•
互斥锁初始化:pthread_mutex_init
•
互斥锁上锁:pthread_mutex_lock
•
互斥锁判断上锁:pthread_mutex_trylock
•
互斥锁接锁:pthread_mutex_unlock
•
消除互斥锁:pthread_mutex_destroy
1.
#include
2.
#include
3.
#include
4.
5.
int
i=0;/*共享变量*/
6.
pthread_mutex_t
mutex=PTHREAD_MUTEX_INITIALIZER;/*互斥锁*/
7.
8.
void
thread1()
9.
{
10.
int
ret;
11.
while(1)
12.
{
13.
14.
15.
ret=pthread_mutex_trylock(&mutex);/*判断上锁*/
16.
17.
if(ret!=EBUSY)
18.
{
19.
pthread_mutex_lock(&mutex);/*上锁*/
20.
printf(This
is
thread1:%d/n,i);
21.
i++;
22.
pthread_mutex_unlock(&mutex);/*解锁*/
23.
}
24.
sleep(1);
25.
}
26.
}
27.
28.
void
thread2()
29.
{int
ret;
30.
while(1)
31.
{
32.
33.
ret=pthread_mutex_trylock(&mutex);
34.
if(ret!=EBUSY)
35.
{
36.
pthread_mutex_lock(&mutex);
37.
printf(This
is
thread2:%d/n,i);
38.
i++;
39.
pthread_mutex_unlock(&mutex);
40.
}
41.
sleep(1);
42.
}
43.
}
44.
int
main()
45.
{
46.
pthread_t
t1,t2;
47.
pthread_mutex_init(&mutex,NULL);
48.
pthread_create(&t1,NULL,(void
*)thread1,NULL);
49.
pthread_create(&t2,NULL,(void
*)thread2,NULL);
50.
51.
pthread_join(t1,NULL);
52.
pthread_join(t2,NULL);
53.
54.
pthread_mutex_destroy(&mutex);
55.
return
0;
56.
}
信号量
未进行同步处理的两个线程
1.
#include
2.
#include
3.
#include
4.
5.
int
i=0;
6.
void
thread1()
7.
{
8.
9.
while(1)
10.
{
11.
printf(This
is
thread1:%d/n,i);
12.
i++;
13.
sleep(1);
14.
}
15.
}
16.
17.
18.
void
thread2()
19.
{
20.
21.
while(1)
22.
{
23.
printf(This
is
thread2:%d/n,i);
24.
i++;
25.
sleep(1);
26.
}
27.
}
28.
29.
int
main()
30.
{
31.
pthread_t
t1,t2;
32.
33.
pthread_create(&t1,NULL,(void
*)thread1,NULL);
34.
pthread_create(&t2,NULL,(void
*)thread2,NULL);
Linux下线程同步的几种方法
Linux 线程同步的三种方法
线程的最大特点是资源的共享性,但资源共享中的同步问题是多线程编程的难点。linux下提供了多种方式来处理线程同步,最常用的是互斥锁、条件变量和信号量。
一、互斥锁(mutex)
通过锁机制实现线程间的同步。
初始化锁。在Linux下,线程的互斥量数据类型是pthread_mutex_t。在使用前,要对它进行初始化。
静态分配:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
动态分配:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_attr_t *mutexattr);
加锁。对共享资源的访问,要对互斥量进行加锁,如果互斥量已经上了锁,调用线程会阻塞,直到互斥量被解锁。
int pthread_mutex_lock(pthread_mutex *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
解锁。在完成了对共享资源的访问后,要对互斥量进行解锁。
int pthread_mutex_unlock(pthread_mutex_t *mutex);
销毁锁。锁在是使用完成后,需要进行销毁以释放资源。
int pthread_mutex_destroy(pthread_mutex *mutex);
[csharp] view plain copy
#include
#include
#include
#include
#include "iostream"
using namespace std;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int tmp;
void* thread(void *arg)
{
cout << "thread id is " << pthread_self() << endl;
pthread_mutex_lock(&mutex);
tmp = 12;
cout << "Now a is " << tmp << endl;
pthread_mutex_unlock(&mutex);
return NULL;
}
int main()
{
pthread_t id;
cout << "main thread id is " << pthread_self() << endl;
tmp = 3;
cout << "In main func tmp = " << tmp << endl;
if (!pthread_create(&id, NULL, thread, NULL))
{
cout << "Create thread success!" << endl;
}
else
{
cout << "Create thread failed!" << endl;
}
pthread_join(id, NULL);
pthread_mutex_destroy(&mutex);
return 0;
}
//编译:g++ -o thread testthread.cpp -lpthread
二、条件变量(cond)
互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。条件变量分为两部分: 条件和变量。条件本身是由互斥量保护的。线程在改变条件状态前先要锁住互斥量。条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。条件的检测是在互斥锁的保护下进行的。如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量可以被用来实现这两进程间的线程同步。
初始化条件变量。
静态态初始化,pthread_cond_t cond = PTHREAD_COND_INITIALIER;
动态初始化,int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
等待条件成立。释放锁,同时阻塞等待条件变量为真才行。timewait()设置等待时间,仍未signal,返回ETIMEOUT(加锁保证只有一个线程wait)
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,const timespec *abstime);
激活条件变量。pthread_cond_signal,pthread_cond_broadcast(激活所有等待线程)
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond); //解除所有线程的阻塞
清除条件变量。无线程等待,否则返回EBUSY
int pthread_cond_destroy(pthread_cond_t *cond);
[cpp] view plain copy
#include
#include
#include "stdlib.h"
#include "unistd.h"
pthread_mutex_t mutex;
pthread_cond_t cond;
void hander(void *arg)
{
free(arg);
(void)pthread_mutex_unlock(&mutex);
}
void *thread1(void *arg)
{
pthread_cleanup_push(hander, &mutex);
while(1)
{
printf("thread1 is running\n");
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
printf("thread1 applied the condition\n");
pthread_mutex_unlock(&mutex);
sleep(4);
}
pthread_cleanup_pop(0);
}
void *thread2(void *arg)
{
while(1)
{
printf("thread2 is running\n");
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
printf("thread2 applied the condition\n");
pthread_mutex_unlock(&mutex);
sleep(1);
}
}
int main()
{
pthread_t thid1,thid2;
printf("condition variable study!\n");
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
pthread_create(&thid1, NULL, thread1, NULL);
pthread_create(&thid2, NULL, thread2, NULL);
sleep(1);
do
{
pthread_cond_signal(&cond);
}while(1);
sleep(20);
pthread_exit(0);
return 0;
}
[cpp] view plain copy
#include
#include
#include "stdio.h"
#include "stdlib.h"
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
struct node
{
int n_number;
struct node *n_next;
}*head = NULL;
static void cleanup_handler(void *arg)
{
printf("Cleanup handler of second thread./n");
free(arg);
(void)pthread_mutex_unlock(&mtx);
}
static void *thread_func(void *arg)
{
struct node *p = NULL;
pthread_cleanup_push(cleanup_handler, p);
while (1)
{
//这个mutex主要是用来保证pthread_cond_wait的并发性
pthread_mutex_lock(&mtx);
while (head == NULL)
{
//这个while要特别说明一下,单个pthread_cond_wait功能很完善,为何
//这里要有一个while (head == NULL)呢?因为pthread_cond_wait里的线
//程可能会被意外唤醒,如果这个时候head != NULL,则不是我们想要的情况。
//这个时候,应该让线程继续进入pthread_cond_wait
// pthread_cond_wait会先解除之前的pthread_mutex_lock锁定的mtx,
//然后阻塞在等待对列里休眠,直到再次被唤醒(大多数情况下是等待的条件成立
//而被唤醒,唤醒后,该进程会先锁定先pthread_mutex_lock(&mtx);,再读取资源
//用这个流程是比较清楚的
pthread_cond_wait(&cond, &mtx);
p = head;
head = head->n_next;
printf("Got %d from front of queue/n", p->n_number);
free(p);
}
pthread_mutex_unlock(&mtx); //临界区数据操作完毕,释放互斥锁
}
pthread_cleanup_pop(0);
return 0;
}
int main(void)
{
pthread_t tid;
int i;
struct node *p;
//子线程会一直等待资源,类似生产者和消费者,但是这里的消费者可以是多个消费者,而
//不仅仅支持普通的单个消费者,这个模型虽然简单,但是很强大
pthread_create(&tid, NULL, thread_func, NULL);
sleep(1);
for (i = 0; i < 10; i++)
{
p = (struct node*)malloc(sizeof(struct node));
p->n_number = i;
pthread_mutex_lock(&mtx); //需要操作head这个临界资源,先加锁,
p->n_next = head;
head = p;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mtx); //解锁
sleep(1);
}
printf("thread 1 wanna end the line.So cancel thread 2./n");
//关于pthread_cancel,有一点额外的说明,它是从外部终止子线程,子线程会在最近的取消点,退出
//线程,而在我们的代码里,最近的取消点肯定就是pthread_cond_wait()了。
pthread_cancel(tid);
pthread_join(tid, NULL);
printf("All done -- exiting/n");
return 0;
}
三、信号量(sem)
如同进程一样,线程也可以通过信号量来实现通信,虽然是轻量级的。信号量函数的名字都以"sem_"打头。线程使用的基本信号量函数有四个。
信号量初始化。
int sem_init (sem_t *sem , int pshared, unsigned int value);
这是对由sem指定的信号量进行初始化,设置好它的共享选项(linux 只支持为0,即表示它是当前进程的局部信号量),然后给它一个初始值VALUE。
等待信号量。给信号量减1,然后等待直到信号量的值大于0。
int sem_wait(sem_t *sem);
释放信号量。信号量值加1。并通知其他等待线程。
int sem_post(sem_t *sem);
销毁信号量。我们用完信号量后都它进行清理。归还占有的一切资源。
int sem_destroy(sem_t *sem);
[cpp] view plain copy
#include
#include
#include
#include
#include
#include
#define return_if_fail(p) if((p) == 0){printf ("[%s]:func error!/n", __func__);return;}
typedef struct _PrivInfo
{
sem_t s1;
sem_t s2;
time_t end_time;
}PrivInfo;
static void info_init (PrivInfo* thiz);
static void info_destroy (PrivInfo* thiz);
static void* pthread_func_1 (PrivInfo* thiz);
static void* pthread_func_2 (PrivInfo* thiz);
int main (int argc, char** argv)
{
pthread_t pt_1 = 0;
pthread_t pt_2 = 0;
int ret = 0;
PrivInfo* thiz = NULL;
thiz = (PrivInfo* )malloc (sizeof (PrivInfo));
if (thiz == NULL)
{
printf ("[%s]: Failed to malloc priv./n");
return -1;
}
info_init (thiz);
ret = pthread_create (&pt_1, NULL, (void*)pthread_func_1, thiz);
if (ret != 0)
{
perror ("pthread_1_create:");
}
ret = pthread_create (&pt_2, NULL, (void*)pthread_func_2, thiz);
if (ret != 0)
{
perror ("pthread_2_create:");
}
pthread_join (pt_1, NULL);
pthread_join (pt_2, NULL);
info_destroy (thiz);
return 0;
}
static void info_init (PrivInfo* thiz)
{
return_if_fail (thiz != NULL);
thiz->end_time = time(NULL) + 10;
sem_init (&thiz->s1, 0, 1);
sem_init (&thiz->s2, 0, 0);
return;
}
static void info_destroy (PrivInfo* thiz)
{
return_if_fail (thiz != NULL);
sem_destroy (&thiz->s1);
sem_destroy (&thiz->s2);
free (thiz);
thiz = NULL;
return;
}
static void* pthread_func_1 (PrivInfo* thiz)
{
return_if_fail(thiz != NULL);
while (time(NULL) end_time)
{
sem_wait (&thiz->s2);
printf ("pthread1: pthread1 get the lock./n");
sem_post (&thiz->s1);
printf ("pthread1: pthread1 unlock/n");
sleep (1);
}
return;
}
static void* pthread_func_2 (PrivInfo* thiz)
{
return_if_fail (thiz != NULL);
while (time (NULL) end_time)
{
sem_wait (&thiz->s1);
printf ("pthread2: pthread2 get the unlock./n");
sem_post (&thiz->s2);
printf ("pthread2: pthread2 unlock./n");
sleep (1);
}
return;
}
pthread_setcancelstate()问题
你先看看这篇文章
http://hi.baidu.com/ganss/blog/item/ff7799f97a87de58242df24c.html
简单理解就是,两个线程T1和T2,如果T1发送cancel信号给T2,则T2默认会在取消点退出。取消点是固定的地方,只有thread_join()、pthread_testcancel()、pthread_cond_wait()、 pthread_cond_timedwait()、sem_wait()、sigwait()等函数以及read()、write()等会引起阻塞的系统调用都是Cancelation-point。
假设T2在一个循环中,这个循环中没有取消点,那么怎么办?收到cancel信号也没办法退出。这时就用pthread_testcancel()来创造一个取消点,如果有cancel信号就退出,没有就继续运行。
那么pthread_setcancelstate就比较号理解了,就是设置T2状态,PTHREAD_CANCEL_ENABLE就是正常处理cancel信号,PTHREAD_CANCEL_DISABLE就是忽略cancel信号。
那每次执行到这段的时候,为什么都没有被杀掉呢?
这里的被杀死指的是别的线程发送cancel信号给他。也许这里就是没发送所以没被杀死
Linux下的线程库pthread库中的pthread_create()函数创建两个线程。
void * thread1() //线程1
{
//............. pthread_mutex_lock(&mut);
a += 1; //①
b = a; //②
pthread_mutex_unlock(&mut);
}
void * thread2() //线程2
{
//.............
pthread_mutex_lock(&mut);
a += 2;
pthread_mutex_unlock(&mut);
}
这样就行呀, 加锁后另一个要等待锁释放
如何终止一个线程?
您好,有三种方法可以使终止线程。1. 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。2. 使用stop方法强行终止线程(这个方法不推荐使用,因为stop和suspend、resume一样,也可能发生不可预料的结果)。3. 使用interrupt方法中断线程。1. 使用退出标志终止线程当run方法执行完后,线程就会退出。但有时run方法是永远不会结束的。如在服务端程序中使用线程进行监听客户端请求,或是其他的需要循环处理的任务。在这种情况下,一般是将这些任务放在一个循环中,如while循环。如果想让循环永远运行下去,可以使用while(true){……}来处理。但要想使while循环在某一特定条件下退出,最直接的方法就是设一个boolean类型的标志,并通过设置这个标志为true或false来控制while循环是否退出。下面给出了一个利用退出标志终止线程的例子。
linux 多进程信号同步问题
线程的最大特点是资源的共享性,但资源共享中的同步问题是多线程编程的难点。linux下提供了多种方式来处理线程同步,最常用的是互斥锁、条件变量和信号量。
1)互斥锁(mutex)
通过锁机制实现线程间的同步。同一时刻只允许一个线程执行一个关键部分的代码。
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutex_attr_t *mutexattr);
int pthread_mutex_lock(pthread_mutex *mutex);
int pthread_mutex_destroy(pthread_mutex *mutex);
int pthread_mutex_unlock(pthread_mutex *
(1)先初始化锁init()或静态赋值pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIER
attr_t有:
PTHREAD_MUTEX_TIMED_NP:其余线程等待队列
PTHREAD_MUTEX_RECURSIVE_NP:嵌套锁,允许线程多次加锁,不同线程,解锁后重新竞争
PTHREAD_MUTEX_ERRORCHECK_NP:检错,与一同,线程请求已用锁,返回EDEADLK;
PTHREAD_MUTEX_ADAPTIVE_NP:适应锁,解锁后重新竞争
(2)加锁,lock,trylock,lock阻塞等待锁,trylock立即返回EBUSY
(3)解锁,unlock需满足是加锁状态,且由加锁线程解锁
(4)清除锁,destroy(此时锁必需unlock,否则返回EBUSY,//Linux下互斥锁不占用内存资源
其他线程如何正确关闭阻塞的Socket线程
UINT DataRecvThreadProc(LPVOID lpParam){C工具Dlg* aDlg = (C工具Dlg*)lpParam;//获取主对话窗的句柄!int len;CString str;extern struct _TestNumRange TestNumRange;if (!AfxSocketInit()){AfxMessageBox(IDP_SOCKETS_INIT_FAILED);return 1;}int nPort = TestNumRange.PortID;;CSocket aSocket;CSocket serverSocket;if (!aSocket.Socket()){AfxMessageBox(_T("创建套接字错误!"));return 1;}BOOL bOptVal = TRUE;int bOptLen = sizeof(BOOL);aSocket.SetSockOpt(SO_REUSEADDR, (void *)&bOptVal, bOptLen, SOL_SOCKET);if (!aSocket.Bind(nPort)){AfxMessageBox(_T("绑定端口失败!"));return 1;}if (!aSocket.Listen(5)){AfxMessageBox(_T("监听失败!"));return 1;}while (m_connect){if (!aSocket.Accept(serverSocket)){continue;}else{unsigned char szRecvMsg[17412] = { 0 };while (1){len = serverSocket.Receive(szRecvMsg, 17412);if (!m_connect) break;if (len == 0 || len == -1){m_connect = false; Recv_Signal = false;if (Recv_Signal) break;serverSocket.Close();aSocket.Close();aDlg->m_pThread = NULL; //在这里把指针NULL掉会不会出错?AfxMessageBox(_T("探头掉线!"));return 0;}if (Recv_Signal){global_CriticalSection.Lock();frashfifo = true;signalFIFO.Input(szRecvMsg, len);global_CriticalSection.Unlock();}}serverSocket.Close();}}aSocket.Close();return 0;}在界面上有两个按钮,启动按钮就是开启这个工作线程,而结束按钮我是这样写的:m_connect = false; Recv_Signal = false;::WaitForSingleObject(m_pThread->m_hThread, 1000); m_pThread = NULL;return;但是这样关闭的话,有一种情况会出错:当工作线程阻塞在len = serverSocket.Receive(szRecvMsg, 17412);的时候,若我按下启动按钮而在这1000ms内Socket没有接收到新的信号,则工作线程无法关闭,而只是把指针NULL了。当我再开启工作线程时,就会出现两个Socket监听同一个端口的情况,这就会导致严重的后果。。。因此,我想知道在这样的情况下我要怎么实现UI去控制工作线程的开关。注:在实际使用中,serverSocket.Receive会有几种接收的情况:5ms一个包,一分钟一个包,因此如果用CEvent的话,我不知道应该怎么设计才能保证不掉包的同时能实现对工作线程的开关控制。
请教用C语言编的借助UDP协议实现的文件传输的程序
server代码
#include;
#include;
#include;
#include;
#include;
#include;
#include;
#include;
#include;
#include;
#include;
#include;
#include;
#define SERV_PORT 2500
#define MAX_SIZE 1024*40
void recvUDP(char name[20],int sockfd)
{
int ret,fd;
mode_t fdmode = (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
char mesg[MAX_SIZE];
fd_set rdset;
struct timeval tv;
int rlen,wlen;
int i;
fd = open(name,O_RDWR|O_CREAT|O_APPEND,fdmode);
if(fd == -1)
{
printf("open file %s error:%n",name,strerror(errno));
exit(-1);
}
for(i=0;;i++)
{
tv.tv_sec = 1;
tv.tv_usec = 0;
FD_ZERO(&rdset);
FD_SET(sockfd,&rdset);
ret = select(sockfd+1,&rdset,NULL,NULL,&tv);
if(ret == -1)
{
printf("select error %s\n",strerror(errno));
exit(-1);
}
else if(ret==0)
{
printf("select timeout,continue circle\n");
continue;
}
if(FD_ISSET(sockfd,&rdset))
{
memset(mesg,0,MAX_SIZE);
rlen = read(sockfd,mesg,MAX_SIZE);
if(rlen <=0 )
{
printf("read error %s\n",strerror(errno));
exit(-1);
}
if(!strcmp(mesg,"end"))
{
printf("recv end.\n");
break;
}
wlen = write(fd,mesg,rlen);
if(wlen != rlen )
{
printf("write error %s\n",strerror(errno));
exit(-1);
}
}
printf("The %d times write\n",i);
}
close(fd);
}
int main(int argc, char *argv[])
{
int sockfd;
int r;
struct sockaddr_in servaddr;
sockfd = socket(AF_INET,SOCK_DGRAM,0); /*create a socket*/
/*init servaddr*/
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
/*bind address and port to socket*/
if(bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) == -1)
{
perror("bind error");
exit(-1);
}
r = fcntl(sockfd, F_GETFL, 0);
fcntl(sockfd, F_SETFL, r & ~O_NONBLOCK);
recvUDP(argv[1],sockfd);
return 0;
}
client代码
#include;
#include;
#include;
#include;
#include;
#include;
#include;
#include;
#include;
#include;
#include;
#include;
#include;
#include;
#define MAX_SIZE 1024*40
#define SERV_PORT 2500
void connectUDP(char name[20],int sockfd,struct sockaddr *pservaddr,socklen_t servlen)
{
char buf[MAX_SIZE];
fd_set wrset;
struct timeval tv;
int rlen,wlen;
int fd;
int ret;
int i;
if(connect(sockfd,(struct sockaddr *)pservaddr,servlen) == -1)
{
perror("connet error");
exit(1);
}
else
printf("connect server ok!\n");
fd = open(name,O_RDONLY);
if(fd==-1)
{
printf("fopen error %s\n",strerror(errno));
exit(-1);
}
i=0;
while(