精通
英语
和
开源
,
擅长
开发
与
培训
,
胸怀四海
第一信赖
当线程已具备安全性时,多个线程之间的同步就变为最重要的部分。同步,是指多个线程之间互相配合,以一定的关联方式使得操作得以连续不断地进行没有使用同步机制去处理共享资源,是导致应用数据被破坏的最常见的原因。
下面列出了常见的同步方式,使用的系统资源由小到大排列:我们可以通过系统的CMPSWP(compare and swap)指令,在多线程程序中访问数据。CMPSWP 指令的语法格式是CMSWP(&operand1, &operand2, swap operand),也就是前两位是变量地址,第三位变量数值。(后面还有扩展参数,先不做深究)
系统根据地址,比较 operand1 与operand2 的值。
如果这两个值相等,那么将 swap operand 的值赋到operand2 中,返回1
如果这两个值不等,那么将operand2 的值将赋到operand1 中,返回0
如果返回1 时(相等),那么系统将会确保在取出operand2 的值来比较和将swap operand的值赋到operand2 这段时间之内,没有别的CMPSWP 指令访问operand2。这也就保障了数据的安全性。
如果返回不等时,并不能保证operand1 不被别的CMPSWP 指令访问。因此只允许operand2 做为并发控制中的共享变量。
Operand1, operand2, swap operand 的长度必须相等,允许长度范围在1、2、4、8 位字节(byte)。
在下面这个例子中,注意对 CMPSWP 命令的使用。
该程序宏定义了一个 ATOMICADD 的函数原型,该函数用来将共享变量var 加上val。
该函数原型中,while 循环的条件,对CMPSWP 命令的返回取反判断,也就是说:
aatemp1 与var 的值相等时,将aatemp2 的值赋值到var 中(这一步是由CMPSWP 命令自动完成),退出循环;
aatemp1 与var 的值不等时,每次都对aatemp2 重新赋值,使其等于当前的aatemp1+val(因为进行完CMPSWP 命令后,当其时的var 的值已赋到aatemp1 中;而且不能将aatemp2赋值为var+val,因为此时的var 的值可能又被其它并发的线程所更改)程序执行结束后,结果应该为 1000000.
如果在线程函数theThread 中,没有使用ATOMICADD 这个函数,而是直接使用
shareData++;这样的语句,那么执行出来的结果将不会是1000000,因为程序中没有对shareData 变量进行保护,当多个线程同时去更改该变量时,实际上只有最后一个更改的生效。
CMPSWP 命令是占用系统资源最小的一种同步机制。
例:#include <mih/cmpswp.h>
#define _MULTI_THREADED
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define ATOMICADD(var, val) { \
int aatemp1 = (var); \
int aatemp2 = aatemp1 + val; \
while( ! _CMPSWP( &aatemp1, &var, aatemp2 ) ) \
aatemp2 = aatemp2 + val; \
}
#define NUMTHREADS 10
#define LOOPCONSTANT 100000
int shareData=0;
void *theThread(void *parm)
{
int loop;
printf("Thread %.8x, %.8x is Entered\n", pthread_getthreadid_np());
for(loop=0;loop<LOOPCONSTANT;loop++)
ATOMICADD(shareData, 1);
return NULL;
}
int main(int argc, char **argv)
{
pthread_t thread[NUMTHREADS];
int rc=0;
int i;
for (i=0; i<NUMTHREADS; i++)
rc = pthread_create(&thread[i], NULL, theThread, NULL);
for (i=0; i <NUMTHREADS;i++)
rc = pthread_join(thread[i], NULL);
printf("Data = %d\n", shareData);
return 0;
}
互斥体 mutex 是Mutual exclusion 的简称。我们可以通过对互斥体的处理,来实现多线程并发时,一个时点上只允许一个线程处理数据的目的。
互斥体的常见操作是 create, lock , unlock ,destory。一个线程成功的对互斥体执行了lock操作后,这个线程就成为了该互斥体的拥有者,直到该线程对互斥体执行unlock 操作。当执行了unlock 操作后,系统就会将互斥体交由另一个排队等待lock 该互斥体的线程中。一个互斥体只能有一个拥有者。
互斥体操作可以递归。递归的互斥体允许拥有者重复地 lock 互斥体。互斥体的拥者将会保持当前状态直到unlock 的请求次数与lock 的请求次数相同。
互斥体可以设置超时等待,也可以设置为立刻返回。
互斥体是我们在进行多线程编程是常用的一种方式。
空间地址锁(space location lock),是一个存放在单字节空间中的逻辑锁。空间地址锁不会改变应用程序所使用到的存储空间,它是系统自身所使用到的一个信息记录片。
空间地址锁用起来,与互斥体有点类似,它与互斥体有几下几个方面的不同:
1、我们可以直接使用空间地址锁来操作数据。空间地址锁不需要应用程序去创建和管理额个的目标。(相比之互斥体就需要创建一个互斥体变量,使用完毕之后还需要销毁掉)。互斥体是作用于线程的,而空间地址锁是直接作用于某个变量的。也就是说它只关注某个变量是否被锁,在使用上与互斥体类似,但在概念与互斥体有较大差异。
2、与互斥体的性能比较。空间地址锁锁住一个共享数据的路径,大概需要500 个RISC 指令(recude instrction set computer),而互斥体只需要50 个;但是空间地址锁不需要创建、销毁等等指令。而互斥体大约需要1000 个RISC 指令。
下面的例子,说明了对空间地址锁的一个简单的用法,主要用到了 locksl,unlosksl 这两个函数。注意空间地址锁直接对关键数据的操作(锁、解锁)。
#define _MULTI_THREADED
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <mih/milckcom.h> /* Lock types */
#include <mih/locksl.h> /* LOCKSL instruction */
#include <mih/unlocksl.h> /* UNLOCKSL instruction */
#define checkResults(string, val) { \
if (val) { \
printf("Failed with %d at %s", val, string); \
exit(1); \
} \
}
#define NUMTHREADS 3
int sharedData=0;
int sharedData2=0;
void *theThread(void *parm)
{
int rc;
printf("Thread %.8x %.8x: Entered\n", pthread_getthreadid_np());
locksl(&sharedData, _LENR_LOCK); /* Lock Exclusive, No Read */
/********** Critical Section *******************/
printf("Thread %.8x %.8x: Start critical section, holding lock\n",
pthread_getthreadid_np());
/* Access to shared data goes here */
++sharedData; --sharedData2;
printf("Thread %.8x %.8x: End critical section, release lock\n",
pthread_getthreadid_np());
unlocksl(&sharedData, _LENR_LOCK); /* Unlock Exclusive, No Read */
/********** Critical Section *******************/
return NULL;
}
int main(int argc, char **argv)
{
pthread_t thread[NUMTHREADS];
int rc=0;
int i;
printf("Enter Testcase - %s\n", argv[0]);
printf("Hold Lock to prevent access to shared data\n");
locksl(&sharedData, _LENR_LOCK); /* Lock Exclusive, No Read */
printf("Create/start threads\n");
for (i=0; i <NUMTHREADS; ++i) {
rc = pthread_create(&thread[i], NULL, theThread, NULL);
checkResults("pthread_create()\n", rc);
}
printf("Wait a bit until we are 'done' with the shared data\n");
sleep(3);
printf("Unlock shared data\n");
unlocksl(&sharedData, _LENR_LOCK); /* Unlock Exclusive, No Read */
printf("Wait for the threads to complete, and release their resources\n");
for (i=0; i <NUMTHREADS; ++i) {
rc = pthread_join(thread[i], NULL);
checkResults("pthread_join()\n", rc);
}
printf("Main completed\n");
return 0;
}