目录
线程安全
创建线程
std::thread 创建线程 (线程不安全)
互斥量(mutex) 原子变量(atomic)
std::thread
thread_local
线程安全
就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取 完,其他线程才可使用。不会出现数据不一致或者数据污染。”
创建线程
std::thread 创建线程 (线程不安全)
#include
#include
using namespace std;
//计数全局变量
long cnt = 0;
//计数程序
void counter()
{
for (int i = 0; i < 100000; ++i)
{
cnt++;
cnt--;
}
}
int main(int argc, char* argv[])
{
std::thread t1(counter);
std::thread t2(counter);
t1.join();
t2.join();
cout << "current cnt = " << cnt<
连续执行两次结果分别是:current cnt = -852 和 current cnt = 27,即这段代码是线程不安全的。
互斥量(mutex) 原子变量(atomic)
#include
#include
#include
using namespace std;
std::mutex mtx;
//计数全局变量
long cnt = 0;
//计数程序
void counter()
{
cout << "当前进程 ID" <thread_local
thread_local变量是C++ 11新引入的一种存储类型。它会影响变量的存储周期(Storage duration),C++中有4种存储周期:
- automatic
- static
- dynamic
- thread
有且只有thread_local关键字修饰的变量具有线程周期(thread duration),这些变量(或者说对象)在线程开始的时候被生成(allocated),在线程结束的时候被销毁(deallocated)。并且每 一个线程都拥有一个独立的变量实例(Each thread has its own instance of the object)。thread_local 可以和static 与 extern关键字联合使用,这将影响变量的链接属性(to adjust linkage)。
那么,哪些变量可以被声明为thread_local?以下3类都是ok的
- 命名空间下的全局变量
- 类的static成员变量
- 本地变量
下面引用《C++ Concurrency in Action》书中的例子来说明这3种情况:
thread_local int x; //A thread-local variable at namespace scope
class X
{
static thread_local std::string s; //A thread-local static class data member
};
static thread_local std::string X::s; //The definition of X::s is required
void foo()
{
thread_local std::vector v; //A thread-local local variable
}
既然每个线程都拥有一份独立的thread_local变量,那么就有2个问题需要考虑:
- 各线程的thread_local变量是如何初始化的
- 各线程的thread_local变量在初始化之后拥有怎样的生命周期,特别是被声明为thread_local的本地变量(local variables)
下面的代码可以帮助回答这2个问题。
#include
thread_local int g_n = 1;
static int g_nn=1;
void f()
{ g_nn++;
g_n++;
printf("f() id=%d, thread_local n=%d, static g_nn=%dn", std::this_thread::get_id(),g_n,g_nn);
}
void foo()
{
thread_local int i=0;
printf("foo() id=%d, n=%dn", std::this_thread::get_id(), i);
i++;
}
void f2()
{
foo();
foo();
}
int main()
{
g_nn++;
g_n++;
f(); //id=1,g_n=3,主线程中,线程id为1, 在f()中g_n = 3
std::thread t1(f); //id=2, n=2,新建线程id为2,新线程初始化g_n为1,通过f()中的g_n++计算,返回的g_n=2
std::thread t2(f); //id=3, n=2,新建线程id为3,新线程初始化g_n为1,通过f()中的g_n++计算,返回的g_n=2
t1.join();
t2.join();
f(); //id=1, n=4 ,回到主线程,id=1,f()再次g_n++,返回g_n=4
f2(); //foo() id=1, n=0,foo() id=1, n=1 回到主线程,id依旧为1,在主线程中初始化i=0
std::thread t4(f2);//foo() id=4, n=0,foo() id=4, n=1 新建线程id=4,初始化i=0
std::thread t5(f2);//foo() id=5, n=0,foo() id=5, n=1 新建线程id=5,初始化i=0
t4.join();
t5.join();
f2(); // 回到主线程id=1,再次执行两次i++,即返回i=2,i=3
return 0;
}
结果:
f() id=1, thread_local n=3, static g_nn=3
f() id=2, thread_local n=2, static g_nn=4
f() id=3, thread_local n=2, static g_nn=5
f() id=1, thread_local n=4, static g_nn=6
foo() id=1, n=0
foo() id=1, n=1
foo() id=4, n=0
foo() id=4, n=1
foo() id=5, n=0
foo() id=5, n=1
foo() id=1, n=2
foo() id=1, n=3
可见,thread_local是规定每次新建线程时,重新初始化该变量,它的声明周期贯穿一个线程的创建和结束。而static 则始终进行一次变量初始化,对所有的线程保持变量共享。
#include#include using namespace std; //计数全局变量 long cnt = 0; //计数程序 void counter() { for (int i = 0; i < 100000; ++i) { cnt++; cnt--; } } int main(int argc, char* argv[]) { std::thread t1(counter); std::thread t2(counter); t1.join(); t2.join(); cout << "current cnt = " << cnt< 连续执行两次结果分别是:current cnt = -852 和 current cnt = 27,即这段代码是线程不安全的。
互斥量(mutex) 原子变量(atomic)
#include#include #include using namespace std; std::mutex mtx; //计数全局变量 long cnt = 0; //计数程序 void counter() { cout << "当前进程 ID" < thread_local thread_local变量是C++ 11新引入的一种存储类型。它会影响变量的存储周期(Storage duration),C++中有4种存储周期:
- automatic
- static
- dynamic
- thread
有且只有thread_local关键字修饰的变量具有线程周期(thread duration),这些变量(或者说对象)在线程开始的时候被生成(allocated),在线程结束的时候被销毁(deallocated)。并且每 一个线程都拥有一个独立的变量实例(Each thread has its own instance of the object)。thread_local 可以和static 与 extern关键字联合使用,这将影响变量的链接属性(to adjust linkage)。
那么,哪些变量可以被声明为thread_local?以下3类都是ok的
- 命名空间下的全局变量
- 类的static成员变量
- 本地变量
下面引用《C++ Concurrency in Action》书中的例子来说明这3种情况:
thread_local int x; //A thread-local variable at namespace scope class X { static thread_local std::string s; //A thread-local static class data member }; static thread_local std::string X::s; //The definition of X::s is required void foo() { thread_local std::vectorv; //A thread-local local variable } 既然每个线程都拥有一份独立的thread_local变量,那么就有2个问题需要考虑:
- 各线程的thread_local变量是如何初始化的
- 各线程的thread_local变量在初始化之后拥有怎样的生命周期,特别是被声明为thread_local的本地变量(local variables)
下面的代码可以帮助回答这2个问题。
#includethread_local int g_n = 1; static int g_nn=1; void f() { g_nn++; g_n++; printf("f() id=%d, thread_local n=%d, static g_nn=%dn", std::this_thread::get_id(),g_n,g_nn); } void foo() { thread_local int i=0; printf("foo() id=%d, n=%dn", std::this_thread::get_id(), i); i++; } void f2() { foo(); foo(); } int main() { g_nn++; g_n++; f(); //id=1,g_n=3,主线程中,线程id为1, 在f()中g_n = 3 std::thread t1(f); //id=2, n=2,新建线程id为2,新线程初始化g_n为1,通过f()中的g_n++计算,返回的g_n=2 std::thread t2(f); //id=3, n=2,新建线程id为3,新线程初始化g_n为1,通过f()中的g_n++计算,返回的g_n=2 t1.join(); t2.join(); f(); //id=1, n=4 ,回到主线程,id=1,f()再次g_n++,返回g_n=4 f2(); //foo() id=1, n=0,foo() id=1, n=1 回到主线程,id依旧为1,在主线程中初始化i=0 std::thread t4(f2);//foo() id=4, n=0,foo() id=4, n=1 新建线程id=4,初始化i=0 std::thread t5(f2);//foo() id=5, n=0,foo() id=5, n=1 新建线程id=5,初始化i=0 t4.join(); t5.join(); f2(); // 回到主线程id=1,再次执行两次i++,即返回i=2,i=3 return 0; } 结果:
f() id=1, thread_local n=3, static g_nn=3
f() id=2, thread_local n=2, static g_nn=4
f() id=3, thread_local n=2, static g_nn=5
f() id=1, thread_local n=4, static g_nn=6
foo() id=1, n=0
foo() id=1, n=1
foo() id=4, n=0
foo() id=4, n=1
foo() id=5, n=0
foo() id=5, n=1
foo() id=1, n=2
foo() id=1, n=3可见,thread_local是规定每次新建线程时,重新初始化该变量,它的声明周期贯穿一个线程的创建和结束。而static 则始终进行一次变量初始化,对所有的线程保持变量共享。



