Skip to content

Instantly share code, notes, and snippets.

@weedge
Last active October 12, 2017 16:46
Show Gist options
  • Save weedge/3f94e9c6144e6b3d11fa55f6801d0e1b to your computer and use it in GitHub Desktop.
Save weedge/3f94e9c6144e6b3d11fa55f6801d0e1b to your computer and use it in GitHub Desktop.
理解spin_lock, mutex_lock,比较两者
// 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;
}
@weedge
Copy link
Author

weedge commented Oct 12, 2017

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,所以是非阻塞的框架。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment