嵌入式 linux下多线程同步问题
2019-07-12 18:35发布
生成海报
现在流行的进程线程同步互斥的控制机制,其实是由最原始最基本的4种方法实现的。由这4种方法组合优化就有了.Net和Java下灵活多变的,编程简便的线程进程控制手段。
1临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。
2互斥量:为协调共同对一个共享资源的单独访问而设计的。
3信号量:为控制一个具有有限数量用户资源而设计。
4事件:用来通知线程有一些事件已发生,从而启动后继任务的开始。
线程的最大特点是资源的共享性,但资源共享中的同步问题是多线程编程的难点。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下互斥锁不占用内存资源
示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#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
说明:pthread库不是Linux系统默认的库,连接时需要使用静态库libpthread.a,所以在使用pthread_create()创建线程,以及调用pthread_atfork()函数建立fork处理程序时,需要链接该库。在编译中要加
-lpthread参数。
2)条件变量(cond)
利用线程间共享的全局变量进行同步的一种机制。条件变量上的基本操作有:触发条件(当条件变为
true 时);等待条件,挂起线程直到其他线程触发条件。
int pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr);
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);
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond); //解除所有线程的阻塞
(1)初始化.init()或者pthread_cond_t
cond=PTHREAD_COND_INITIALIER(前者为动态初始化,后者为静态初始化);属性置为NULL
(2)等待条件成立.pthread_wait,pthread_timewait.wait()释放锁,并阻塞等待条件变量为真,timewait()设置等待时间,仍未signal,返回ETIMEOUT(加锁保证只有一个线程wait)
(3)激活条件变量:pthread_cond_signal,pthread_cond_broadcast(激活所有等待线程)
(4)清除条件变量:destroy;无线程等待,否则返回EBUSY
对于
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
一定要在mutex的锁定区域内使用。
如果要正确的使用pthread_mutex_lock与pthread_mutex_unlock,请参考
pthread_cleanup_push和pthread_cleanup_pop宏,它能够在线程被cancel的时候正确的释放mutex!
另外,posix1标准说,pthread_cond_signal与pthread_cond_broadcast无需考虑调用线程是否是mutex的拥有者,也就是说,可以在lock与unlock以外的区域调用。如果我们对调用行为不关心,那么请在lock区域之外调用吧。
说明:
(1)pthread_cond_wait
自动解锁互斥量(如同执行了pthread_unlock_mutex),并等待条件变量触发。这时线程挂起,不占用CPU时间,直到条件变量被触发(变量为ture)。在调用
pthread_cond_wait之前,应用程序必须加锁互斥量。pthread_cond_wait函数返回前,自动重新对互斥量加锁(如同执行了pthread_lock_mutex)。
(2)互斥量的解锁和在条件变量上挂起都是自动进行的。因此,在条件变量被触发前,如果所有的线程都要对互斥量加锁,这种机制可保证在线程加锁互斥量和进入等待条件变量期间,条件变量不被触发。条件变量要和互斥量相联结,以避免出现条件竞争——个线程预备等待一个条件变量,当它在真正进入等待之前,另一个线程恰好触发了该条件(条件满足信号有可能在测试条件和调用pthread_cond_wait函数(block)之间被发出,从而造成无限制的等待)。
(3)pthread_cond_timedwait
和 pthread_cond_wait
一样,自动解锁互斥量及等待条件变量,但它还限定了等待时间。如果在abstime指定的时间内cond未触发,互斥量mutex被重新加锁,且pthread_cond_timedwait返回错误
ETIMEDOUT。abstime
参数指定一个绝对时间,时间原点与
time 和gettimeofday
相同:abstime = 0
表示 1970年1月1日00:00:00
GMT。
(4)pthread_cond_destroy
销毁一个条件变量,释放它拥有的资源。进入 pthread_cond_destroy
之前,必须没有在该条件变量上等待的线程。
(5)条件变量函数不是异步信号安全的,不应当在信号处理程序中进行调用。特别要注意,如果在信号处理程序中调用
pthread_cond_signal
或pthread_cond_boardcast
函数,可能导致调用线程死锁。
示例程序1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#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 runningn"
);
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond,&mutex);
printf(
"thread1 applied the conditionn"
);
pthread_mutex_unlock(&mutex);
sleep(
4
);
}
pthread_cleanup_pop(
0
);
}
void
*thread2(
void
*arg)
{
while
(
1
)
{
printf(
"thread2 is runningn"
);
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond,&mutex);
printf(
"thread2 applied the conditionn"
);
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
;
}
示例程序2:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#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;
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
;
}
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮