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;
}
运行结果

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

同时对文件操作导致未知的数据出现在文件中。
原文:https://www.cnblogs.com/area-h-p/p/11599652.html