线程是计算机中独立运行的最小单位,运行占用资源很小,宏观上线程同时执行。微观上通过系统分配时间片去的CPU控制权交替执行线程中的代码。当然多核情况下是可以并行的。
目录
- 线程优点
- 线程私有数据
- 线程的同步方法
线程的优点
1.节省资源,节省时间。多进程时,每个进程都拥有独立的空间,而同进程下的多线程共享进程的地址空间。同时也节省了为其分配空间所花费的时间。
2.因为地址空间共享,所以线程间的切换速度远快于进程。
3.线程共享空间,通信更加方便。
私有数据
1.线程ID。唯一标识,基本调用阿关闭阿传信号阿都是用它。
2.寄存器。包括程序计数器和堆栈指针。
3.堆栈。
4.信号掩码。
优先级。
线程私有的存储空间
线程私有数据可以被线程内各个函数访问,但对其他线程是屏蔽的。
线程私有数据采用一键多值的技术,访问数据时通过键值来访问的,就是好像在对一个变量进行访问,其实是在访问不同的数据,所以在使用私有数据前,要先为每个线程创建一个相关联的键。
测试代码:
#include <stdio.h>
#include <string.h>
#include <pthread.h>
pthread_key_t key;
void *thread2(void *arg)
{
int tsd = 5;
printf("thread %d is running\n", pthread_self());
pthread_setspecific(key, (void *)tsd);
printf("thread %d return %d\n", pthread_self(), pthread_getspecific(key));
}
void *thread1( void *arg )
{
int tsd = 0;
pthread_t thid2;
printf("thread %d is running\n", pthread_self());
pthread_setspecific( key, (void *)tsd );
pthread_create(&thid2, NULL, thread2, NULL);
sleep(2);
printf("thread %d return %d\n", pthread_self(), pthread_getspecific(key));
}
int main( int argc, char *argv )
{
pthread_t thid1;
printf("main thread begigs running\n");
pthread_key_create( &key, NULL );
pthread_create( &thid1, NULL, thread1, NULL );
sleep(3);
pthread_key_delete( key );
printf("main thread exit\n");
return 0;
}
主要函数:
//从TSD池中分配一项,将值赋给key,destr_function为清理函数
int pthread_key_create( pthread_key_t *key, void (*destr_function)(void *));
//将pointer的值与key相关联
int pthread_setspecific(pthread_key_t key, const void *pointer);
//得到与key相关联的数据
void *pthread_getsspecific( pthread_key_t key);
//用来删除键,释放键所占用的内存
int pthread_key_delete(pthread_key_t key);
运行结果:
代码中,main函数创建线程1,线程1创建线程2,他们共用全局变量key为键值,但是私有数据tsd不同。主要函数是线程内部函数pthread_setspecific(key, tsb)将key键和tsb值相关联,实现一键多值。
线程创建
有时需要控制线程仅执行一次,下面的代码尝试实现这个要求。
#include<stdio.h>
#include <pthread.h>
pthread_once_t once = PTHREAD_ONCE_INIT;
void run(void)
{
printf("Function run is running in thread %u\n", pthread_self()); //pthread_self()函数得到当前线程的ID
}
void *thread1(void *arg)
{
printf("Current thread's id is %u\n", pthread_self());
pthread_once(&once, run); //此函数用来保证函数执行一次
printf("thread1 ends\n");
}
void *thread2(void *arg)
{
printf("Current thread's id is %u\n", pthread_self());
pthread_once(&once, run);
printf("thread2 end\n");
}
int main()
{
pthread_t thid1, thid2;
pthread_create(&thid1, NULL, thread1, NULL);
pthread_create(&thid2, NULL, thread2, NULL);
sleep(3);
printf("main thread exit!\n");
exit(0);
}
运行结果:
可以看出函数run中的Function run…了一次。
线程同步
线程最大的特点就是同步性。但是同步时资源分配问题就成了难题。linux下提供互斥锁,条件变量,和异步信号来处理这个问题。
互斥锁
锁的使用比较简单,利用pthread_mutex_init()初始化一个互斥锁就可以使用了,使用方法如下:
pthread mutex_t number_mutex; //互斥锁
pthread_mutex_lock(&number_mutex); //加锁
globalnumber ++; //某全局变量
pthread_mutex_unlock(&number_mutex); //解锁
当使用pthread_mutex_lock()加锁时,使用全部变量过程中,别的线程就无权访问它,除非解锁。
来看几个相关函数:
pthread_mutex_init(); 初始化一个互斥锁
pthread_mutex_destroy(); 注销一个互斥锁
pthread_mutex_lock(); 加锁,如果不成功,阻塞等待
pthread_mutex_unlock(); 解锁
pthread_mutex_trylock(); 尝试加锁。如果不成功立即返回,错误码为EBUSY
条件变量
条件变量宏观上类似if语句,符合条件就能执行某段程序,否则只能等待条件成立。
使用条件变量主要包括两个动作,一个等待使用资源的线程等待“条件变量被设置为真”,另一个线程在使用完资源后,“设置条件为真”。
条件变量一般配合互斥锁使用。
主要函数:
pthread_cond_init(); //初始化条件变量
pthread_cond_wait(); //基于条件变量阻塞,无条件等待
pthread_cond_timewait(); // 阻塞直到时间发生,计时等待
pthread_cond_signal(); // 解决特定线程的等待
pthread_cond_broadcast(); // 解除所有线程的阻塞
pthread_cond_destory(); //清除条件变量
异步信号
信号与任何线程都是异步的,也就是说信号到达线程的时间是不定的,
处理异步信号的函数:
//向线程threadid发送信号SIGNO
int pthread_kill(pthread_t threadid, int signo);
//设置线程的信号屏蔽码
int pthread_sigmask(int how, const sigset_t *newmask, sigset_t *oldmask);
//阻塞线程,等待set中某一信号到达,将其存入*sig
int sigwait(const sigset_t *set, int *sig);