Last active
October 12, 2017 16:46
-
-
Save weedge/3f94e9c6144e6b3d11fa55f6801d0e1b to your computer and use it in GitHub Desktop.
理解spin_lock, mutex_lock,比较两者
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Name: spinlockvsmutex1.cpp | |
//http://www.parallellabs.com/2010/01/31/pthreads-programming-spin-lock-vs-mutex-performance-analysis/ | |
// Source: http://www.alexonlinux.com/pthread-mutex-vs-pthread-spinlock | |
// Compiler(spin lock version): g++ -o spin_version -DUSE_SPINLOCK spinlockvsmutex1.cpp -lpthread | |
// Compiler(mutex version): g++ -o mutex_version spinlockvsmutex1.cpp -lpthread | |
#include <stdio.h> | |
#include <unistd.h> | |
#include <sys/syscall.h> | |
#include <errno.h> | |
#include <sys/time.h> | |
#include <list> | |
#include <pthread.h> | |
#define LOOPS 5000000 | |
using namespace std; | |
list<int> the_list; | |
#ifdef USE_SPINLOCK | |
pthread_spinlock_t spinlock; | |
#else | |
pthread_mutex_t mutex; | |
#endif | |
//Get the thread id | |
pid_t gettid() { return syscall( __NR_gettid ); } | |
void *consumer(void *ptr) | |
{ | |
int i; | |
printf("Consumer TID %lu \n", (unsigned long)gettid()); | |
while (1) | |
{ | |
#ifdef USE_SPINLOCK | |
pthread_spin_lock(&spinlock); | |
#else | |
pthread_mutex_lock(&mutex); | |
#endif | |
if (the_list.empty()) | |
{ | |
#ifdef USE_SPINLOCK | |
pthread_spin_unlock(&spinlock); | |
#else | |
pthread_mutex_unlock(&mutex); | |
#endif | |
break; | |
} | |
i = the_list.front(); | |
the_list.pop_front(); | |
#ifdef USE_SPINLOCK | |
pthread_spin_unlock(&spinlock); | |
#else | |
pthread_mutex_unlock(&mutex); | |
#endif | |
} | |
return NULL; | |
} | |
int main() | |
{ | |
int i; | |
pthread_t thr1, thr2; | |
struct timeval tv1, tv2; | |
#ifdef USE_SPINLOCK | |
pthread_spin_init(&spinlock, 0); | |
#else | |
pthread_mutex_init(&mutex, NULL); | |
#endif | |
// Creating the list content... | |
for (i = 0; i < LOOPS; i++) | |
the_list.push_back(i); | |
// Measuring time before starting the threads... | |
gettimeofday(&tv1, NULL); | |
pthread_create(&thr1, NULL, consumer, NULL); | |
pthread_create(&thr2, NULL, consumer, NULL); | |
pthread_join(thr1, NULL); | |
pthread_join(thr2, NULL); | |
// Measuring time after threads finished... | |
gettimeofday(&tv2, NULL); | |
if (tv1.tv_usec > tv2.tv_usec) | |
{ | |
tv2.tv_sec--; | |
tv2.tv_usec += 1000000; | |
} | |
printf("Result - %ld.%ld \n", tv2.tv_sec - tv1.tv_sec, | |
tv2.tv_usec - tv1.tv_usec); | |
#ifdef USE_SPINLOCK | |
pthread_spin_destroy(&spinlock); | |
#else | |
pthread_mutex_destroy(&mutex); | |
#endif | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
pi@I_like_pi:~/c $ time ./mutex_version
Consumer TID 21264
Consumer TID 21265
Result - 3.899746
real 0m6.370s
user 0m5.380s
sys 0m2.470s
pi@I_like_pi:~/c $ time ./spin_version
Consumer TID 21279
Consumer TID 21278
Result - 2.339544
real 0m4.778s
user 0m6.430s
sys 0m0.250s
由上可以看出用mutex_lock(互斥锁,独占式,悲观锁)在这个队列生产消费的操作中比spin_lock(自旋锁,乐观锁)执行效率要低,
主要是系统调用上比较耗时,因为如果要阻塞或唤醒一个线程,都需要操作系统来帮忙完成,这就需要从用户态转换到核心态中,
因此状态转换需要耗费很多的处理器时间。
像java中的synchronized是独占式的悲观锁,所以只是简单的读/写类的成员变量,就不需要用synchronized,用volatile 就行了,
因为volatile 修饰的对象,JIT编译器生成的汇编指令其中写指令会加上lock前缀;而lock前缀的指令在多核处理器下会引发了两件事情:
1.将当前处理器缓存行的数据会写回到系统内存。
2.这个写回内存的操作会引起在其他CPU里缓存了该内存地址的数据无效。
从而保证操作的原子性;也不会引起上下文的切换和调度。
java中的CAS自旋就是一种自旋锁, 在java1.5版本及以上的并发框架java.util.concurrent 的atmoic包下的类基本都是自旋锁的实现,由于原理是CAS,所以是非阻塞的框架。