- **避免阻塞**
大家知道,单个进程只有一个主线程,当主线程阻塞的时候,整个进程也就阻塞了,无法再去做其它的一些功能了。
- 避免CPU空转
应用程序经常会涉及到RPC,数据库访问,磁盘IO等操作,这些操作的速度比CPU慢很多,而在等待这些响应时,CPU却不能去处理新的请求,导致这种单线程的应用程序性能很差。
- 提升效率
一个进程要独立拥有4GB的虚拟地址空间,而多个线程可以共享同一地址空
间,线程的切换比进程的切换要快得多。
- 上下文切换
头文件:#include
线程具有单独的执行流,因此需要单独定义线程的main函数,还需要请求操作系统在单独的执行流中执行该函数,完成该功能的函数如下。
//线程创建→成功时返回 0,失败时返回其他值。
int pthread_create(
pthread_t*restrict thread,
const pthread_attr_t * restrict attr,
void *(* start_routine)(void *),
void*restrict arg
);
●thread
保存新创建线程ID的变量地址值。线程与进程相同,也需要用于区分不同线程的ID。
●attr
用于传递线程属性的参数,传递NULL时,创建默认属性的线程。
●start_routine
相当于线程main函数的、在单独执行流中执行的函数地址值(函数指针)。
● arg
通过第三个参数传递调用函数时包含传递参数信息的变量地址值。
调用pthread_join函数的进程(或线程)将进入等待状态,直到第一个参数为ID的线程终止为止。而且可以得到线程的main函数返回值,所以该函数比较有用。下面通过示例了解该函数的功能。
# include
//线程等待→成功时返回线程的int,失败时返回其他值。
int pthread_join(pthread_t thread, void ** status);
#include
void* threadEntry(void* arg)
{
const char* msg = "I am from thread!";
for (int i = 0; i < 3; i++)
{
printf("%s(%d):%s thread begin=%s!\r\n", __FILE__, __LINE__, __FUNCTION__, arg);
}
return (void*)msg;
}
void thread_func()
{
pthread_t tid;
const char* pInfo = "hello thread!";
int ret = pthread_create(&tid, NULL, threadEntry, (void*)pInfo);//编译时需要加上 -lpthread,Linux系统默认已有,window下需要自己下载
if (ret != -1)
{
void* result = NULL;
pthread_join(tid, &result);//线程等待,等待tid线程结束再执行下面内容
printf("%s(%d):%s from thread=%s!\r\n", __FILE__, __LINE__, __FUNCTION__, result);
}
}
线程同步用于解决线程访问顺序引发的问题。需要同步的情况可以从如下两方面考虑。
"控制(Control)线程执行顺序"的相关内容。假设有A、B两个线程,线程A负责向指定内存空间写人(保存)数据,线程B负责取走该数据。这种情况下,线程A首先应该访问约定的内存空间并保存数据。万一线程B先访问并取走数据,将导致错误结果。像这种需要控制执行顺序的情况也需要使用同步技术。
#include
int pthread_mutex_init(pthread mutex_t*mutex, const pthread_mutexattr_t* attr);
int pthread_mutex_destroy(pthread_mutex_t * mutex);
//→成功时返回0,失败时返回其他值。
#include
int pthread_mutex_lock(pthread_mutex_t* mutex);
int pthread_mutex_unlock(pthread mutex_t* mutex);
//→成功时返回0,失败时返回其他值。
简言之,就是利用lock和unlock函数围住临界区的两端。此时互斥量相当于一把锁,阻止多个线程同时访问。还有一点需要注意,线程退出临界区时,如果忘了调用pthread mutex_unlock 函数,那么其他为了进入临界区而调用pthread mutexlock函数的线程就无法摆脱阻塞状态。这就是死锁状态。
#include
#include
#include
#include
#define NUM_THREAD 100
void * thread_inc(void * arg);
void * thread_des(void * arg);
long long num=0;
pthread_mutex_t mutex;
int main(int argc, char *argv[])
{
pthread_t thread_id[NUM_THREAD];
int i;
pthread_mutex_init(&mutex, NULL);
for(i=0; i<NUM_THREAD; i++)
{
if(i%2)
pthread_create(&(thread_id[i]), NULL, thread_inc, NULL);
else
pthread_create(&(thread_id[i]), NULL, thread_des, NULL);
}
for(i=0; i<NUM_THREAD; i++)
pthread_join(thread_id[i], NULL);
printf("result: %lld \n", num);
pthread_mutex_destroy(&mutex);
return 0;
}
void * thread_inc(void * arg)
{
int i;
pthread_mutex_lock(&mutex);
for(i=0; i<50000000; i++)
num+=1;
pthread_mutex_unlock(&mutex);
return NULL;
}
void * thread_des(void * arg)
{
int i;
for(i=0; i<50000000; i++)
{
pthread_mutex_lock(&mutex);
num-=1;
pthread_mutex_unlock(&mutex);
}
return NULL;
}
信号量创建及销毁方法如下:
#include
/*→成功时返回0,失败时返回其他值。*/
//创建
int sem_init(sem_t* sem, int pshared, unsigned int value);
//销毁
int sem_destroy(sem_t* sem);
上述函数的pshared参数默认向其传递0。接下来介绍信号量中相当于互斥量lock、unlock的函数。
#include
int sem_post(sem_t*sem);
int sem_wait(sem_t * sem);
//→成功时返回0,失败时返回其他值。
调用sem init函数时,操作系统将创建信号量对象,此对象中记录着"信号量值"整数。该值在调用sem post函数时增1,调用sem_wait函数时减1。但信号量的值不能小于0,因此,在信号量为0的情况下调用sem_wait函数时,调用函数的线程将进入阻塞状态(因为函数未返回)。
当然,此时如果有其他线程调用sem_post函数,信号量的值将变为1,而原本阻塞的线程可以将该信号量重新减为0并跳出阻塞状态。实际上就是通过这种特性完成临界区的同步操作,可以通过如下形式同步临界区(假设信号量的初始值为1)。
sem_wait(&sem);//信号量变为0…
// 临界区的开始
//…
//临界区的结束
sem_post(&sem);// 信号量变为1…
#include
#include
#include
void * read(void * arg);
void * accu(void * arg);
static sem_t sem_one;
static sem_t sem_two;
static int num;
int main(int argc, char *argv[])
{
pthread_t id_t1, id_t2;
sem_init(&sem_one, 0, 0);//是否完成输入
sem_init(&sem_two, 0, 1);//是否完成计算
pthread_create(&id_t1, NULL, read, (void) 5);
pthread_create(&id_t2, NULL, accu, (void) 5);
pthread_join(id_t1, NULL);
pthread_join(id_t2, NULL);
sem_destroy(&sem_one);
sem_destroy(&sem_two);
return 0;
}
void * read(void * arg)
{
int i;
int count = (int)arg;
for(i=0; i<count; i++)
{
fputs("Input num: ", stdout);
sem_wait(&sem_two);
scanf("%d", &num);
sem_post(&sem_one);
}
return NULL;
}
void * accu(void * arg)
{
int sum=0, i;
int count = (int)arg;
for(i=0; i<count; i++)
{
sem_wait(&sem_one);
sum+=num;
sem_post(&sem_two);
}
printf("Result: %d \n", sum);
return NULL;
}
#include
int pthread_detach(pthread_t thread);
//→成功时返回,失败时返回其他值。
thread 终止的同时需要销毁的线程ID。
调用 pthread_detach函数不会引起线程终止或进入阻塞状态,可以通过该函数引导销毁线程创建的内存空间。
调用该函数后不能再针对相应线程调用pthread_join函数,这需要格外注意。虽然还有方法在创建线程时可以指定销毁时机,但与pthread_detach方式相比,结果上没有太大差异.
此外可以通过 线程函数:
pthread_detach(pthread_self());
…
pthread_exit(0) ;
来进行自动销毁,无需外部的操作