锐英源软件
第一信赖

精通

英语

开源

擅长

开发

培训

胸怀四海 

第一信赖

当前位置:锐英源 / 开源技术 / 多线程的同步
服务方向
人工智能数据处理
人工智能培训
kaldi数据准备
小语种语音识别
语音识别标注
语音识别系统
语音识别转文字
kaldi开发技术服务
软件开发
运动控制卡上位机
机械加工软件
软件开发培训
Java 安卓移动开发
VC++
C#软件
汇编和破解
驱动开发
联系方式
固话:0371-63888850
手机:138-0381-0136
Q Q:396806883
微信:ryysoft

多线程的同步

当线程已具备安全性时,多个线程之间的同步就变为最重要的部分。同步,是指多个线程之间互相配合,以一定的关联方式使得操作得以连续不断地进行没有使用同步机制去处理共享资源,是导致应用数据被破坏的最常见的原因。

下面列出了常见的同步方式,使用的系统资源由小到大排列:
1. CMPSWP 指令 Compare and swap
2. 互斥体 Mutual exclusion(mutexes) and threads
3. 信号量 Semaphores and threads
4. 条件变量与线程 Condition variable and threads
5. 线程的同步单元 Threads as synchronization primitives
6. 空间锁 Space location lock
7. 目标锁 Object lock

CMPSWP

我们可以通过系统的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;
}
友情链接
版权所有 Copyright(c)2004-2021 锐英源软件
公司注册号:410105000449586 豫ICP备08007559号 最佳分辨率 1024*768
地址:郑州大学北校区院(文化路97号院)内