线程控制2

3.私有数据(一键多值技术)

多线程环境下,进程内的所有线程共享进程的数据空间,因此全局变量为所有线程公有。有些时候,需要保存线程自己的全局变量,这个全局变量仅在某个线程内有效,各个函数均可以访问该线程的私有全局变量,这个就是一键多值技术,即一个键对应多个数值。

访问数据时都是通过键值来访问,看起来是在访问一个变量,实际在访问不同的数据。

#include<pthread.h>
int pthread_key_create(pthread_key_t *key, void (*dest_function)(void *));//创建一个键
int pthread_setspecific(pthread_key_t key, const void *pointer);//为一个键设置线程私有数据
void *pthread_getspecific(pthread_key_t key);//从一个键读取线程私有数据
int pthread_key_delete(pthread_key_t key);//删除一个键

 pthread_key_create:从Linux的TSD池中分配一项,将其赋值给key供后访问使用,第一个参数key为指向键值的指针,第二个参数为一个函数指针,如果指针不为空,则在线程退出时将以2key所关联的数据为参数调用destr_function,释放分配的缓冲区。

key值一旦被创建,所有线程都可以访问他,各个线程可以根据自己的需要向key中填值,相当于提供了一个同名而不同值的全局变量,一键多值。

pthread_setspecific:该函数将pointer的值与key值相关联,用pthread_setspecific为一个键值指定新的线程函数时,线程必须先释放原有的线程数据以回收空间。

pthread_getspecific:通过该函数得到与key相关联的数据。

pthread_key_delete:该函数用来删除一个键,删除后,键所占用的内存将被释放。注意当键占用的内存被释放后,与该键关联的线程数据所占用的内存并不会被释放。因此,线程数据的释放必须在释放键之前完成。

例程

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>

pthread_key_t key;


void func()
{
	printf("key's value is %d\n", pthread_getspecific(key));
}
void *thread2(void *arg)
{
	int tsd = 5;
	printf("thread %ld is running\n", pthread_self());
	pthread_setspecific(key, (void*)tsd);
	printf("thread2 %ld return %d\n", pthread_self(), pthread_getspecific(key));
	func();
}


void *thread1(void *arg)
{
	int tsd = 0;
	pthread_t thid2;
	printf("thread %ld is running\n", pthread_self());
	pthread_create(&thid2, NULL, thread2, NULL);
	sleep(2);
	printf("thread1 %ld returns %d\n",pthread_self(), pthread_getspecific(key));
	func();
}
int main()
{
	pthread_t thid;
	printf("main thread begins running\n");
	pthread_key_create(&key, NULL);
	pthread_create(&thid, NULL, thread1, NULL);
	sleep(3);
	pthread_key_delete(key);
	printf("main thread exit\n");
	return 0;
}

 运行结果

 

 可以看到,每个线程在调用func函数时返回的仍然是自己线程设置的key值。

4.线程同步

4.1互斥锁

多线程通过为关键代码加锁的方式来实现线程间的同步。

互斥锁函数

#include<pthread.h>
pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);//初始化一个互斥锁
pthread_mutex_destory(pthread_mutex_t *mutex);//注销一个互斥锁
pthread_mutex_unlock(pthread_mutex_t *mutex);//加锁,若不成功,阻塞等待
pthread_mutex_unlock(pthread_mutex_t *mutex);//解锁
pthread_mutex_trylock(pthread_mutex_t *mutex);//测试加锁,若不成功立即返回,错误码为EBUSY

 使用互斥锁之前必须先进行初始化操作。初始化的方式有两种,一种是静态赋值法,将宏常量PTHREAD_MUTEX_INITIALIZER赋值给互斥锁

pthread_mutex_t mutex = PTHREAD_MUTEX_INITILIZER;

 另外一种方式就是通过pthread_mutex_init函数初始化互斥,函数原型如上

参数mutexattr表示互斥锁的属性,如果为NULL则使用默认属性。

初始化结束后就可以使用pthread_mutex_lock,pthread_mutex_trylock这两个函数给互斥锁加锁了。

用pthread_mutex_lock()加锁时,如果mutex已经被锁住,当前尝试加锁的线程就会阻塞,直至互斥锁被其他线程释放。当pthread_mutex_lock函数返回时,说明互斥锁已经被当前线程加锁成功。pthread_mutex_lock则不同,若mutex已经被加锁,他将立即返回,返回的错误码为EBUSY,而不是阻塞等待。

注:加锁时,无论何种类型的锁,都不可能同时被两个不同的线程同时获得,其中一个必须等待解锁。在同一进程中的线程,若加锁后没有解锁,其他线程将无法再获得该锁。

锁用完后应当解锁,使用pthread_mutex_unlock函数解锁时,要满足两个条件:一个是互斥锁必须处于加锁状态,二是调用本函数的线程必须是给互斥锁加锁的线程(即加锁前锁还在,谁加锁谁解锁)。

互斥锁使用完毕后,必须进行清除。清除互斥锁使用函数pthread_mutex_destory,函数原型如上。

清除一个互斥锁将会释放它所占用的资源。清除互斥锁时要求锁处于放开的状态。若锁处于锁定状态,函数返回EBUSY,该函数成功执行时返回0。由于在Linux中,互斥锁并不占用内存,因此pthread_mutex_destory()除了解除互斥锁的状态外再无任何作用。

例程(文件读写保护)

#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>

#define File  "output.txt"

pthread_mutex_t number_mutex;

void *pthread1(void *arg)
{
	int t = 5;
	while(t)
	{
		write_max(File);
		sleep(1);
	 	--t;
	}
}

void *pthread2(void *arg)
{
	int t = 5;
	while(t)
	{
		read_max(File);
		sleep(1);
		--t;
	}
}
void write_max(char *file)
{
	unsigned long long a = 0;
	int b = 0;
	char timeStr[14], ch;
	time_t timer;
	struct tm *tblock;
	time(&timer);
	tblock = gmtime(&timer);
	a = (tblock->tm_year+1900)*100;
	a = (a+tblock->tm_mon+1)*100;
	a = (a+tblock->tm_mday)*100;
	a = (a+tblock->tm_hour+8)*100;
	a = (a+tblock->tm_min)*100;
	a = (a+tblock->tm_sec);
	sprintf(timeStr, "%llu", a);
	pthread_mutex_lock(&number_mutex);
	FILE *fp;
	fp = fopen(file, "w");
	if(fp == NULL)
	{
		printf("Open file failed\n");
		fclose(fp);
		exit(1);
	}
	while(timeStr[b] != '\0')
	{
		fputc(timeStr[b], fp);
		++b;
	}
	printf("pthread = %ld writing %s to %s\n", pthread_self(), timeStr, file);
	fclose(fp);
	pthread_mutex_unlock(&number_mutex);
}
void read_max(char *file)
{
	char ch[14], *rc = NULL;
	pthread_mutex_lock(&number_mutex);
	FILE *fp;
	fp = fopen(file, "r");
	if(fp == NULL)
	{
		printf("Open file error\n");
		fclose(fp);
		exit(1);
	}
	rc = fgets(ch, 15, fp);
	printf("pthread = %ld, time = %s\n", pthread_self(), ch);
	fclose(fp);
	pthread_mutex_unlock(&number_mutex);
}


int main()
{
	pthread_t th1, th2;
	pthread_create(&th1, NULL, pthread1, NULL);
	pthread_create(&th2, NULL, pthread2, NULL);
	sleep(6);
	return 0;
}

 运行结果

 当程序中多个线程都要对一个文件进行读写操作的时候,为保证同步和文件安全,在一个线程对文件进行操作时必须加锁,其余要使用文件的线程先等待锁被释放后随机获得互斥锁。

(将写和读程序的加锁解锁代码都注释以后)

 同时对文件操作导致未知的数据出现在文件中。



猜你喜欢

转载自www.cnblogs.com/area-h-p/p/11599652.html