看如下代码
#test.cc
int i=0;
void* funcA(void* arg)
{
for(int i=0;i<10;i++)
{
++i;
}
}
void* funcB(void* arg)
{
for(inti=0;i<10;i--)
{
--i;
}
}
int main()
{
pthread_create(..funcAdd..);
pthread_create(..funcAdd..);
cout << i << endl;
return 0;
}
多线程并发更新全局变量i的值,最终的结果是不确定的。
要想得到预期结果0,需要在对变量i进行递增或者递减操作时,加锁处理。
代码如下:
#test.cc
pthread_mutex_t m_i;
int i=0;
void* funcA(void* arg)
{
for(int i=0;i<10;i++)
{
lock(&m_i);
++i;
unlock(&m_i);
}
}
void* funcB(void* arg)
{
for(inti=0;i<10;i--)
{
lock(&m_i);
--i;
unlock(&m_i);
}
}
int main()
{
pthread_create(..funcAdd..);
pthread_create(..funcAdd..);
cout << i << endl;
}
但是加锁等待,被唤醒,解锁这一系列操作,意味着需要进行频繁的上下文切换,影响性能。为了避免这个问题,gcc4.1.2版本之后,提供了一系列的原子操作,也就说,不需要引入锁的保护,来实现对变量的并发操作。
MySQL对这些操作进行了封装,InnoDB内部spin lock的实现也是利用了原子操作,server层也提供了同样的原子操作。
来看一个MySQL server层的示例:Global_THD_manager中表示当前处于running状态的线程数的成员变量,如下:
volatile int32 num_thread_running;
每当有一个线程从sleep状态转为运行状态时,都需要让num_thread_running递增,而结束运行时,需要递减,这就和本文开始的场景完全一样。如果通过锁来保护num_thread_running变量,在高并发下必定会影响性能。
负责递增和递减num_thread_running变量的两个函数
void inc_thread_running()
{
my_atomic_add32(&num_thread_running, 1);
}
/**
Decrements thread running statistic variable.
*/
void dec_thread_running()
{
my_atomic_add32(&num_thread_running, -1);
}
其中my_atomic_add32的实现如下:
static inline int32 my_atomic_add32(int32 volatile *a, int32 v)
{
return __sync_fetch_and_add(a, v);
}
它通过__sync_fetch_and_add函数来实现对变量a的add操作。
下图是MySQL提供的其他原子操作集合
别的模块不直接使用过__sync_fetch_and_add等一系列的函数,而是使用封装过后的以my*打头的函数。
sun_ashe
发布了165 篇原创文章 · 获赞 77 · 访问量 11万+
私信
关注
标签:thread,void,原子,running,num,MySQL,操作,Server,my
来源: https://blog.csdn.net/sun_ashe/article/details/104172716