http://www.parallellabs.com/2010/01/31/pthreads-programming-spin-lock-vs-mutex-performance-analysis/
POSIX threads(简称Pthreads)是在多核平台上进行并行编程的一套常用的API。线程同步(Thread Synchronization)是并行编程中非常重要的通讯手段,其中最典型的应用就是用Pthreads提供的锁机制(lock)来对多个线程之间共 享的临界区(Critical Section)进行保护(另一种常用的同步机制是barrier)。
Pthreads提供了多种锁机制:
(1) Mutex(互斥量):pthread_mutex_***
(2) Spin lock(自旋锁):pthread_spin_***
(3) Condition Variable(条件变量):pthread_con_***
(4) Read/Write lock(读写锁):pthread_rwlock_***
Pthreads提供的Mutex锁操作相关的API主要有:
pthread_mutex_lock (pthread_mutex_t *mutex);
pthread_mutex_trylock (pthread_mutex_t *mutex);
pthread_mutex_unlock (pthread_mutex_t *mutex);
Pthreads提供的与Spin Lock锁操作相关的API主要有:
pthread_spin_lock (pthread_spinlock_t *lock);
pthread_spin_trylock (pthread_spinlock_t *lock);
pthread_spin_unlock (pthread_spinlock_t *lock);
从实现原理上来讲,Mutex属于sleep-waiting类型的锁。例如在一个双核的机器上有两个线程(线程A和线程B),它们分别运行在 Core0和Core1上。假设线程A想要通过pthread_mutex_lock操作去得到一个临界区的锁,而此时这个锁正被线程B所持有,那么线程 A就会被阻塞(blocking),Core0 会在此时进行上下文切换(Context Switch)将线程A置于等待队列中,此时Core0就可以运行其他的任务(例如另一个线程C)而不必进行忙等待。而Spin lock则不然,它属于busy-waiting类型的锁,如果线程A是使用pthread_spin_lock操作去请求锁,那么线程A就会一直在 Core0上进行忙等待并不停的进行锁请求,直到得到这个锁为止。
如果大家去查阅Linux glibc中对pthreads API的实现NPTL(
001// Name: spinlockvsmutex1.cc
002// Source: http://www.alexonlinux.com/pthread-mutex-vs-pthread-spinlock
003// Compiler(spin lock version): g++ -o spin_version -DUSE_SPINLOCK spinlockvsmutex1.cc -lpthread
004// Compiler(mutex version): g++ -o mutex_version spinlockvsmutex1.cc -lpthread
005#include <stdio.h>
006#include <unistd.h>
007#include <sys/syscall.h>
008#include <errno.h>
009#include <sys/time.h>
010#include <list>
011#include <pthread.h>
012
013#define LOOPS 50000000
014
015
using namespace std;
016
017
list<int> the_list;
018
019#ifdef USE_SPINLOCK
020pthread_spinlock_t spinlock;
021#else
022pthread_mutex_t mutex;
023#endif
024
025//Get the thread id
026
pid_t gettid() { return syscall( __NR_gettid ); }
027
028
void *consumer(void *ptr)
029{
030
int i;
031
032
printf("Consumer TID %lun", (unsigned long)gettid());
033
034
while (1)
035
{
036#ifdef USE_SPINLOCK
037
pthread_spin_lock(&spinlock);
038#else
039
pthread_mutex_lock(&mutex);
040#endif
041
042
if (the_list.empty())
043
{
044#ifdef USE_SPINLOCK
045
pthread_spin_unlock(&spinlock);
046#else
047
pthread_mutex_unlock(&mutex);
048#endif
049
break;
050
}
051
052
i = the_list.front();
053
the_list.pop_front();
054
055#ifdef USE_SPINLOCK
056
pthread_spin_unlock(&spinlock);
057#else
058
pthread_mutex_unlock(&mutex);
059#endif
060
}
061
062
return NULL;
063}
064
065
int main()
066{
067
int i;
068
pthread_t thr1, thr2;
069
struct timeval tv1, tv2;
070
071#ifdef USE_SPINLOCK
072
pthread_spin_init(&spinlock, 0);
073#else
074
pthread_mutex_init(&mutex, NULL);
075#endif
076
077
// Creating the list content...
078
for (i = 0; i < LOOPS; i++)
079
the_list.push_back(i);
080
081
// Measuring time before starting the threads...
082
gettimeofday(&tv1, NULL);
083
084
pthread_create(&thr1, NULL, consumer, NULL);
085
pthread_create(&thr2, NULL, consumer, NULL);
086
087
pthread_join(thr1, NULL);
088
pthread_join(thr2, NULL);
089
090
// Measuring time after threads finished...
091
gettimeofday(&tv2, NULL);
092
093
if (tv1.tv_usec > tv2.tv_usec)
094
{
095
tv2.tv_sec--;
096
tv2.tv_usec += 1000000;
097
}
098
099
printf("Result - %ld.%ldn", tv2.tv_sec - tv1.tv_sec,
100
tv2.tv_usec - tv1.tv_usec);
101
102#ifdef USE_SPINLOCK
103
pthread_spin_destroy(&spinlock);
104#else
105
pthread_mutex_destroy(&mutex);
106#endif
107
108
return 0;
109}