线程同步的原因: 解决数据访问冲突问题.
(一), 线程同步的粗浅认识: 线程同步主要是为了完成线程间数据共享和同步, 保持数据的完整性.(比如在多线程中, 一些敏感的数据部允许被多个线程同时访问, 此时就使用同步访问技术, 保证数据在任何时刻, 最多有一个线程访问, 以保证数据的完整性. PS: 在进行多线程编程, 最头疼的就是那些共享的数据. 因为你无法知道哪个线程会在哪个时候对它进行操作, 你也无法得知那个线程会先运行, 哪个线程会后运行, 通过以下这些技术可以合理安排线程之间对资源的竞争:)
(二,)解决线程同步的主要技术:
1, 使用互斥量
2, 使用信号量
3, 使用条件变量
(三)相关技术的介绍
1, 什么时候用互斥量????
在一个进程中一些变量或资源某时刻只能有一个线程对其访问的时候就必须实用互斥量来进行控制. 比如有一个全局链表(看如下代码), 有几个工作线程, 每个线程从该链表中取出表头结点, 然后对该头结点进行处理. 比如现在线程 1 正在取出头节点, 他的操作如下(所谓的关键代码)
- Item * p =queue_list;(取链表的头结点)
- queue_list=queue_list->next;
- Process_job(p);
- Free(p);
当线程 1 处理完第一步, 也就是 Item *p=queue_list 后, 这时候系统停止线程 1 的运行, 改而运行线程 2. 线程 2 照样取出头节点, 然后进行处理, 最后释放了该节点. 过了段时间, 线程 1 重新得到运行. 而这个时候, 其实 p 所指向的节点已经被线程 2 释放掉, 而线程 1 对此毫无知晓. 他会接着运行 process_job(p). 而这将导致无法预料的后果! 在这种情况下就必须用互斥量来进行控制.
2, 互斥量实现的细节:
(1), 首先申明一个 pthread_mutex_t my_mutex 类型的变量 PTHREAD_MUTEX_INITIALIZER;
(2), 在关键代码之前使用 pthread_mutex_lock(&my_mutex); 来对互斥量进行加锁.
(3)关键代码
(4)在关键代码完成的时候使用 pthread_mutex_unlock(&my_mutex)对互斥量进行解锁.
2, 为什么要使用条件变量????
考虑如下情况: 如果一线程都在等待一种信号, 如果该信号被设置, 则线程继续执行, 否则不停的去查询该信号是否被设置, 知道检测到信号被设置了之后才往下执行. 这样将会浪费很多的资源, 所以在这种情况下就需要使用条件变量来进行处理. 通过条件变量, 我们就可以将等待信号的线程阻塞, 直到有信号的时候再去唤醒它.
3, 条件变量的实现细节:
(1), 首先申明一个 pthread_cond_t 类型的变量, 然后进行初始化. 如 pthread_cond_t my_cond=PTHREAD_COND_INITIALIZER;
(2), 如果信号被设置 (比如对一个全局链表进行操作的时候, 加入了一个节点的时候) 就调用 pthread_cond_signal(&my_cond)(该函数参数是条件变量功能是唤醒某个线程)函数来发出信号, 如果此时有线程在等待该信号, 那么该线程将被唤醒, 如果没有, 该信号就会被忽略. 如果当有信号的时候要唤醒所有线程的时候就使用函数: pthread_cond_broadcast(); 来实现.
(3), 在对线程在对信号进行处理的时候 (比如在处理链表的时候, 如果链表不是空的, 则进行处理. 如果链表是空的则就调用 wait 函数来进行阻塞线程), 如果有相应的信号就直接执行下去, 如果没有信号发生则调用 pthread_cond_wait(&my_cond,&my_mutex) 等待信号(该函数的第一个参数是条件变量, 第二个参数是一个 mutex. 在调用该函数之前必须先获得互斥量. 如果线程阻塞, 互斥量将立刻会被释放.)
细节: 在使用条件变量对两个或多个线程进行同步的时候, 在 pthread_cond_wait 之前要对互斥量进行加锁, 当程序执行到 pthread_cond_wait()的时候将要执行以下原子操作:
(1)对互斥量解锁.
(2)挂起线程, 等待条件变量满足, 被唤醒.
(3)在条件满足从而离开 pthread_cond_wait()之前, mutex 将被重新加锁, 以与进入 pthread_cond_wait()前的加锁动作对应.
这样就可以解释为什么在 pthread_cond_signal 之前对互斥变量加锁时, 能够获取到锁, 而不产生死锁.
(三)条件变量与互斥锁, 信号量的区别
1. 互斥锁必须总是由给它上锁的线程解锁, 信号量的挂出即不必由执行过它的等待操作的同一进程执行. 一个线程可以等待某个给定信号灯, 而另一个线程可以挂出该信号灯.
2. 互斥锁要么锁住, 要么被解开(二值状态, 类型二值信号量).
3. 由于信号量有一个与之关联的状态(它的计数值), 信号量挂出操作总是被记住. 然而当向一个条件变量发送信号时, 如果没有线程等待在该条件变量上, 那么该信号将丢失.
4. 互斥锁是为了上锁而设计的, 条件变量是为了等待而设计的, 信号灯即可用于上锁, 也可用于等待, 因而可能导致更多的开销和更高的复杂性.
PS: 关于信号量方面的知识暂时还没有研究, 在这里就不谈了.
来源: http://www.bubuko.com/infodetail-3138657.html