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; }
运行结果
当程序中多个线程都要对一个文件进行读写操作的时候,为保证同步和文件安全,在一个线程对文件进行操作时必须加锁,其余要使用文件的线程先等待锁被释放后随机获得互斥锁。
(将写和读程序的加锁解锁代码都注释以后)
同时对文件操作导致未知的数据出现在文件中。