多线程与多进程的概念
说到多线程编程,那么就不得不提并行和并发,多线程是实现并发(并行)的一种手段。并行是指两个或多个独立的操作同时进行。注意这里是同时进行,区别于并发,在一个时间段内执行多个操作。在单核时代,多个线程是并发的,在一个时间段内轮流执行;在多核时代,多个线程可以实现真正的并行,在多核上真正独立的并行执行。例如现在常见的4核4线程可以并行4个线程;4核8线程则使用了超线程技术,把一个物理核模拟为2个逻辑核心,可以并行8个线程。
并发编程
通常,要实现并发有两种方法:多进程和多线程。
多进程并发
使用多进程并发是将一个应用程序划分为多个独立的进程(每个进程只有一个线程),这些独立的进程间可以互相通信,共同完成任务。由于操作系统对进程提供了大量的保护机制,以避免一个进程修改了另一个进程的数据,使用多进程比多线程更容易写出安全的代码。但这也造就了多进程并发的两个缺点:
- 在进程件的通信,无论是使用信号、套接字,还是文件、管道等方式,其使用要么比较复杂,要么就是速度较慢或者两者兼而有之。
- 运行多个线程的开销很大,操作系统要分配很多的资源来对这些进程进行管理。
由于多个进程并发完成同一个任务时,不可避免的是:操作同一个数据和进程间的相互通信,上述的两个缺点也就决定了多进程的并发不是一个好的选择。
多线程并发
多线程并发指的是在同一个进程中执行多个线程。有操作系统相关知识的应该知道,线程是轻量级的进程,每个线程可以独立的运行不同的指令序列,但是线程不独立的拥有资源,依赖于创建它的进程而存在。也就是说,**同一进程中的多个线程共享相同的地址空间,可以访问进程中的大部分数据,指针和引用可以在线程间进行传递。**这样,同一进程内的多个线程能够很方便的进行数据共享以及通信,也就比进程更适用于并发操作。由于缺少操作系统提供的保护机制,在多线程共享数据及通信时,就需要程序员做更多的工作以保证对共享数据段的操作是以预想的操作顺序进行的,并且要极力的避免死锁(deadlock)
互斥体
互斥体:
任何写过多线程程序的人都知道避免不同线程同时访问共享区域的重要性。如果一个线程要改变共享区域中某个数据,而与此同时另一线程正在读这个数据,那么结果将是未定义的。为了避免这种情况的发生就要使用一些特殊的原始类型和操作。其中最基本的就是互斥体(mutex,mutual exclusion的缩写)。一个互斥体一次只允许一个线程访问共享区。当一个线程想要访问共享区时,首先要做的就是锁住(lock)互斥体。如果其他的线程已经锁住了互斥体,那么就必须先等那个线程将互斥体解锁,这样就保证了同一时刻只有一个线程能访问共享区域。
互斥体的概念有不少变种。Boost线程库支持两大类互斥体,包括简单互斥体(simple mutex)和递归互斥体(recursive mutex)。如果同一个线程对互斥体上了两次锁,就会发生死锁(deadlock),也就是说所有的等待解锁的线程将一直等下去。有了递归互斥体,单个线程就可以对互斥体多次上锁,当然也必须解锁同样次数来保证其他线程可以对这个互斥体上锁。
形象理解:https://blog.csdn.net/galaxyxupt/article/details/81613181
自己的理解:互斥体相当于在其所在作用域加了一扇门,如果让多个线程均通过这扇门来访问此作用域中的数据,则可以通过是否给门(互斥体)上锁,来保证该作用域的数据被多个线程单独访问。
- mutex是对象类,lock是模板类。
- 常用的互斥体有:
boost::mutex
boost::timed_mutex
boost::shared_mutex
boost::recursive_mutex - lock的类模版有:
boost::unique_lock
boost::shared_lock
对类模版特例化,可以生成不同的lock类。
简单代码(体会多线程、互斥体、锁的概念)
#include <boost/thread.hpp>
#include <iostream>
int m=10;
boost::mutex io_mutex;
void task1() {
// do stuff
boost::mutex::scoped_lock
lock(io_mutex);
m=m*2;
std::cout << "This is task1! "<<m << std::endl;
}
void task2() {
// do stuff
boost::mutex::scoped_lock
lock(io_mutex);
m+=2;
std::cout << "This is task2! "<<m << std::endl;
}
int main (int argc, char ** argv) {
using namespace boost;
thread thread_1 = thread(task1);
thread thread_2 = thread(task2);
// do other stuff
thread_2.join();
thread_1.join();
return 0;
}
编译指令:
g++ example.cpp -lboost_system -lboost_thread -o example
需指出要包含库的路径和链接库的名称。
通过给互斥体上锁,保证了两个线程对m的单独访问,但两个线程谁先访问无法确定,线程1由于在前面,先访问的概率较大。
条件变量(配合互斥体一起使用)
https://blog.csdn.net/liujiayu2/article/details/50587099