线程的实现方式
在操作系统中,线程的实现有以下三种方式:
- 内核级线程
- 用户级线程
- 组合级线程
Linux 中线程的实现:
Linux 实现线程的机制非常独特。从内核的角度来说,它并没有线程这个概念。 Linux 把所有的线程都当做进程来实现。内核并没有准备特
别的调度算法或是定义特别的数据结构来表征线程。相反,线程仅仅被视为一个与其他进程共享某些资源的进程。每个线程都拥有唯一隶属
于自己的 task_struct,所以在内核中,它看起来就像是一个普通的进程(只是线程和其他一些进程共享某些资源,如地址空间)。
线程库中的接口介绍
#include <pthread.h>
/*
pthread_create()用于创建线程
thread: 接收创建的线程的 ID
attr: 指定线程的属性
start_routine: 指定线程函数
arg: 给线程函数传递的参数
成功返回 0, 失败返回错误码
*/
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
/*
pthread_exit()退出线程
retval:指定退出信息 向外界传递的参数
*/
int pthread_exit(void *retval);
/*
pthread_join()等待 thread 指定的线程退出,线程未退出时,该方法阻塞
retval:接收 thread 线程退出时,指定的退出信息
*/
int pthread_join(pthread_t thread, void **retval);
线程同步
线程同步指的是当一个线程在对某个临界资源进行操作时,其他线程都不可以对这个资源进行操作,直到该线程完成操作, 其他线程才
能操作,也就是协同步调,让线程按预定的先后次序进行运行。 线程同步的方法有四种:互斥锁、信号量、条件变量、读写锁。
编写下列程序,将index在五个进程上一起加1000次
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<pthread.h>
int _index=1;
void *thread_fun(void * arg)
{
for(int i=0; i<1000; i++)
{
printf("_index=%d\n",_index++);
}
}
int main()
{
pthread_t id[5];
int i=0;
for(;i<5; i++ )
{
pthread_create(&id[i],NULL,thread_fun,NULL);
}
for(i=0; i<5; i++)
{
pthread_join(id[i],NULL);
}
exit(0);
}
第一次运行:
第二次运行:
第三次运行:
说明线程在对index的加操作上不是完全独立的,如果两个线程同时对index进行加操作,那index的值增加就会少一次,所以每次运行的值都不一样。
对程序添加上信号量,每个线程进行index加操作都要PV操作,这样index值就会固定,线程就同步了
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<pthread.h>
#include<semaphore.h>
sem_t sem;
int _index=1;
void *thread_fun(void * arg)
{
for(int i=0; i<1000; i++)
{
sem_wait(&sem);//P
printf("_index=%d\n",_index++);
sem_post(&sem);//V
}
}
int main()
{
sem_init(&sem,0,1);
pthread_t id[5];
int i=0;
for(;i<5; i++ )
{
pthread_create(&id[i],NULL,thread_fun,NULL);
}
for(i=0; i<5; i++)
{
pthread_join(id[i],NULL);
}
exit(0);
sem_destroy(&sem);
}
运行结果:
互斥锁
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t *attr);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
信号量
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);
int sem_destroy(sem_t *sem);
条件变量
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *attr);
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_signal(pthread_cond_t *cond); //唤醒单个线程
int pthread_cond_broadcast(pthread_cond_t *cond); //唤醒所有等待的线程
int pthread_cond_destroy(pthread_cond_t *cond);
读写锁
#include <pthread.h>
int pthread_rwlock_init(pthread_rwlock_t *rwlock, pthread_rwlockattr_t *attr);
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
线程安全
多线程程序无论调度顺序怎样,都能得到一个正确的结果,就处于一个线程安全状态
要保证线程安全需要做到:
1) 对线程同步,保证同一时刻只有一个线程访问临界资源。
2) 在多线程中使用线程安全的函数(可重入函数),所谓线程安全的函数指的是:如果一个
函数能被多个线程同时调用且不发生竟态条件,则我们程它是线程安全的。