线程的概念及使用
线程:进程内一个相对独立的、可调度的执行单元,是系统独立调度和分配CPU的基本单位(指运行中程序的调度单位)
1.线程内核对象:操作系统用它来管理堆栈,存放线程的统计信息
2.线程堆栈:用于维护线程在执行代码时,需要的所有函数参数和局部变量
简单的说,单核的cpu一次只能运行一个线程,但是可以通过时间片的调度,依次执行多个线程,但由于执行时间短,让人有同时执行多线程的错觉.
问题:由于不同的操作系统对线程的处理有不同的API,例如线程的创建,在windows下调用函数CreatThread(),而在ubuntu下使用pThread().显然这会造成很大的麻烦,于是在C11的新特性中规范了线程的函数.
注意:本文的代码都是ubuntu16.04 下的cmake工程
#include <iostream>
#include <thread>
using namespace std;
void foo() {
std::cout << "hello world2" << std::endl;
}
int main()
{
std::thread t(foo); //创建一个新的子线程 回调函数foo
t.join(); //等待线程执行完毕
return 0;
}
线程同步问题及产生的原因
操作系统是通过CPU的时间片轮转来实现多线程的,每个线程有着对应的时间片,当其时间片到来时CPU会切换到该线程上下文并执行,待时间片结束后切换至下一线程,保存原线程的上下文并加载下一线程的上下文,依次循环。
但是,CPU何时切换并不是由用户决定,当时间片到达后会立即进行线程的切换,那么当多个线程并发进行读写操作时,就可能出现线程同步问题。即当一个线程在执行共享数据的多条代码过程中,其他线程参与了运算,就会导致线程安全问题的产生
#include <iostream>
#include <thread>
using namespace std;
int g_nData = 0;
void foo() {
for (int i = 0; i < 100000; i++) {
g_nData++;
}
}
int main()
{
std::thread t(foo);
for (int i = 0; i < 100000; i++) {
g_nData++;
}
t.join();
std::cout << g_nData << std::endl;
return 0;
}
线程同步问题的解决思路
原子操作:
是指线程在访问资源时能够确保所有其他线程都不在同一时间内访问相同的资源。
简单的举个例子:
对于甲组和乙组(不同线程)使用同一个会议室(共享数据)开会
为了避免开会冲突的情况,在会议室装了一把锁(同一把锁)
当甲组(线程一)开会时,在会议室上锁(独占会议室)
直到甲组开会结束,对会议室进行解锁
当乙组想开会开会时,若此时甲组在开会,此时会议室门被锁上,无法进去开会
直到甲组结束对会议室进行解锁,乙组才可以使用会议室开会
解决线程同步问题(只讨论C11,不讨论具体系统)
在c++11中对创建临界区、上锁解锁等操作实现了API的封装,需要包含头文件mutex
std::mutex g_mtx; 定义锁(同一共享数据使用同一把锁)
g_mtx.lock();上锁
g_mtx.unlock(); 解锁
当然c11提供了更简介的方式实现对锁的控制:std::lock_guard<std::mutex> lg(g_mtx);
在需要上锁的地方定义即可,lock_guard和unique_lock的区别可以参考以下链接:
https://www.cnblogs.com/fnlingnzb-learner/p/9542183.html
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
int g_nData = 0;
//创建临界区对象--等价于锁
std::mutex g_mtx;
void foo() {
{
std::lock_guard<std::mutex> lg(g_mtx);
//g_mtx.lock();
//进来上锁(颗粒度)
for (int i = 0; i < 100000; i++) {
g_nData++;
}
//出去解锁
//g_mtx.unlock();
}
}
int main()
{
std::thread t(foo);
//进来上锁
{ //利用块作用域限制锁的作用域 ,降低颗粒度
std::lock_guard<std::mutex> lg(g_mtx);
//g_mtx.lock();
for (int i = 0; i < 100000; i++) {
g_nData++;
}
//g_mtx.unlock();
}
t.join();
std::cout << g_nData << std::endl;
return 0;
}
以上代码对应的CMakeLists.txt
project(thread1)
cmake_minimum_required(VERSION 2.8)
set(CMAKE_BUILD_TYPE Release)
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall")
set(CMAKE_CXX_FLAGS_RELEASE "-std=c++11 -O3 -fopenmp -pthread")
aux_source_directory(. SRC_LIST)
add_executable(${PROJECT_NAME} ${SRC_LIST})