互斥量跟临界区很相似,只有拥有互斥对象的线程才具有访问资源的权限,由于互斥对象只有一个,因此就决定了任何情况下此共享资源都不会同时被多个线程所访问。当前占据资源的线程在任务处理完后应将拥有的互斥对象交出,以便其他线程在获得后得以访问资源。互斥量比临界区复杂。因为使用互斥不仅仅能够在同一应用程序不同线程中实现资源的安全共享,而且可以在不同应用程序的线程之间实现对资源的安全共享。
下面是一个在Linux下使用Mutex的多线程例子。在该例中,进程创建了10个线程,而这10个线程都会访问全局变量counter,对其执行加1操作。为此来分析在加锁和不加锁保护全局变量counter的情况下,counter的值的变化。
#include <stdio.h>
#include <pthread.h>
pthread_mutex_t mutex;
//互斥量声明
int counter = 0; //互斥量保护的全局变量
void *thread_function(void *dummyPtr)
{
printf("Thread number %ld\n",
pthread_self());
pthread_mutex_lock( &mutex1 );
counter++;
pthread_mutex_unlock( &mutex1 );
}
int main(int argc, char *argv[])
{
pthread_t thread_id[10];
int i, j;
pthread_mutex_init(&mutex, NULL);
for(i=0; i < 10; i++)
{
pthread_create(
&thread_id[i], NULL, thread_function, NULL );
}
for(j=0; j < 10; j++)
{
pthread_join(
thread_id[j], NULL);
}
*/
printf("Final counter value: %d\n",
counter);
return 0;
}
下面来分析一下在使用同步互斥锁与不使用时,程序运行与数据改变情况(具体数据的改变请参照表1和表2):
1) 无互斥锁的情况:
int counter = 0;
/* Function */
void function()
{
counter++;
}
表1 不使用同步互斥锁时,程序运行与数据改变情况
线程1 |
线程2 |
counter = 0 |
counter = 0 |
counter = 1 |
counter = 1 |
2) 有互斥锁的情况:
int counter = 0;
/* Function */
void function()
{
pthread_mutex_lock(&mutex1);
counter++;
pthread_mutex_unlock(&mutex1);
}
表2 使用同步互斥锁时,程序运行与数据改变情况
线程1 |
线程2 |
Counter=0 |
Counter=0 |
Counter=1 |
线程1独占访问counter。
线程2不能访问 |
|
Counter=2 |
通过比较看出,在无互斥锁时,线程1访问counter变量,counter值增1。同时,线程2也访问了counter变量,而此时由于counter变量的初始值可能为0也可能为1,所以在线程2中counter变量在执行counter++语句后值可能为1或者为2。而在加入互斥锁之后,在线程1独占访问counter变量之时,线程2不能同时访问counter变量。只有当线程1释放了互斥锁之后,线程2才能访问counter变量并实行加1操作,这个时候,线程1已经使counter的值为1了,所以在线程2中counter的值将由1变为2。所以,在不加锁的时候,程序运行结果会出现其数据结果不确定性和不能准确反映程序执行真实结果的情况。而在加了锁之后,线程序列化独占访问共享数据,保证了共享数据的一致性和完整性。
WINDOWS平台的mutex使用:
#include <string>
#include <iostream>
#include <Windows.h>
using namespace std;
HANDLE hMutex = NULL;
int arr[5] = {1, 2, 3, 4, 5};
DWORD CALLBACK Thread1(LPVOID Param)
{
while(1)
{
WaitForSingleObject(hMutex, INFINITE);
cout << "Thread1:\t";
for (int i = 0; i < 5; i++)
{
cout << arr[i] << ' ';
Sleep(50);
}
cout << endl;
ReleaseMutex(hMutex);
}
return 0;
}
DWORD CALLBACK Thread2(LPVOID Param)
{
while(1)
{
WaitForSingleObject(hMutex, INFINITE);
cout << "Thread2:\t";
for (int i = 0; i < 5; i++)
{
cout << arr[i] << ' ';
Sleep(50);
}
cout << endl;
ReleaseMutex(hMutex);
}
return 0;
}
int main()
{
HANDLE hArray[2] = {0};
hMutex =
CreateMutex(NULL, NULL, NULL);
hArray[0]=CreateThread(NULL,0,Thread1,NULL,0,NULL);
hArray[1]=CreateThread(NULL,0,Thread2,NULL,0,NULL);
WaitForMultipleObjects(2,
hArray, TRUE, INFINITE);
CloseHandle(hArray[0]);
CloseHandle(hArray[1]);
CloseHandle(hMutex);
return 0;
}
Copyright 2011-2020 © MallocFree. All rights reserved.