1.线程是进程内部的一条执行序列或执行路径,一个进程可以包含多条线程。
线程是一个进程内部的一个控制序列。
线程的实现有以下三种方式:
内核级线程:创建开销大,由内核直接管理,可以使用多个处理器
用户级线程:创建开销小,可以创建很多,无法使用多个处理器
组合级线程
1.1为什么需要线程?
<1>.提高应用程序的响应速度,并发操作时使用
<2>.使用线程提高CPU利用率
1.2线程函数
/*
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);//创建
//void *(*start_routine) (void *) 函数指针,返回一个void类型的参数
/*
pthread_exit()退出线程
retval:指定退出信息
*/
int pthread_exit(void *retval);//退出
/*
pthread_join()等待 thread 指定的线程退出,线程未退出时,该方法阻塞
retval:接收thread 线程退出时,指定的退出信息
*/
int pthread_join(pthread_t thread, void **retval);//0等待
2.多线程代码
#include <pthread.h>
void * pthread_fun(void *arg)
{
int i = 0;
for(; i < 5; ++i)
{
sleep(1);
printf("fun thread running\n");
}
pthread_exit("fun over");
}
int main()
{
pthread_t tid;
int res = pthread_create(&tid, NULL, pthread_fun, NULL);
assert(res == 0);
int i = 0;
for(; i < 5; ++i)
{
sleep(1);
printf("main thread running\n");
}
char *s = NULL;
pthread_join(tid, (void **)&s);
printf("s = %s\n", s);
}
gcc时需要加库
多线程有多种执行结果,不能只看见一种场景就认为一定那一种就是正确的.
3.线程并发运行
void* thread_fun(void *arg)
{
int index = *((int*)arg);
int i = 0;
for( ;i < 5; i++ )
{
printf("index=%d\n",index);
sleep(1);
}
}
int main()
{
pthread_t id[5];
int i = 0;
for( ; i < 5; i++ )
{
pthread_create(&id[i], NULL, thread_fun, (void*)&i);
}
for( i = 0;i < 5; i++ )
{
pthread_join(id[i],NULL);
}
exit(0);
}
图片17
并发执行意味着上述程序中main函数中第一个for循环五个i同时执行并打印,并不是之前所理解的按照顺序依次执行【就好比现在是5个人同时写卷子,并且同时交卷;以前学的是五个人玩“击鼓传花”】
int g = 0;
void* thread_fun(void *arg)
{
int i = 0;
for( ;i < 1000; i++ )
{
printf("g=%d\n", ++g);
}
}
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);
}
出现的结果大致为小于等于5000,
4.线程同步
线程同步指的是当一个线程在对某个临界资源进行操作时,其他线程都不可以对这个资源进行操作,直到该线程完成操作,其他线程才能操作,也就是协同步调,让线程按预定的先后次序进行运行。线程同步的方法有四种:互斥锁、信号量、条件变量、读写锁。
4.1互斥锁
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);
线程和函数线程模拟访问打印机,主线程输出第一个字符‘a’表示开始使用打印机,输出第二个字符‘a’表示结束使用,函数线程操作与主线程相同。(由于打印机同一时刻只能被一个线程使用,所以输出结果不应该出现 abab)
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
pthread_mutex_t mutex;
void * thread_fun(void* arg)
{
int i = 0;
for( ;i < 5; i++ )
{
pthread_mutex_lock(&mutex);
write(1,"B",1);
int n = rand() % 3;
sleep(n);
write(1,"B",1);
pthread_mutex_unlock(&mutex);
n = rand() % 3;
sleep(n);
}
pthread_exit(NULL);
}
int main()
{
pthread_t id;
pthread_mutex_init(&mutex,NULL);
pthread_create(&id,NULL, thread_fun,NULL);
int i = 0;
for( ;i < 5; i++ )
{
pthread_mutex_lock(&mutex);
write(1,"A",1);
int n = rand() % 3;
sleep(n);
write(1,"A",1);
pthread_mutex_unlock(&mutex);
n = rand() % 3;
sleep(n);
}
pthread_join(id,NULL);
pthread_mutex_destroy(&mutex);
exit(0);
}
4.2信号量
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_destrory(sem_t *sem);
示例:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <fcntl.h>
char buff[128] = {0};
sem_t sem1;
sem_t sem2;
void* PthreadFun(void *arg)
{
int fd = open("a.txt", O_RDWR | O_CREAT, 0664);
assert(fd != -1);
//函数线程完成将用户输入的数据存储到文件中
while(1)
{
sem_wait(&sem2);
if(strncmp(buff, "end", 3) == 0)
{
break;
}
write(fd, buff, strlen(buff));
memset(buff, 0, 128);
sem_post(&sem1);
}
sem_destroy(&sem1);
sem_destroy(&sem2);
}
int main()
{
sem_init(&sem1, 0, 1);
sem_init(&sem2, 0, 0);
pthread_t id;
int res = pthread_create(&id, NULL, PthreadFun, NULL);
assert(res == 0);
//主线程完成获取用户数据的数据,并存储在全局数组 buff 中
while(1)
{
sem_wait(&sem1);
printf("please input data: ");
fflush(stdout);
fgets(buff, 128, stdin);
buff[strlen(buff) - 1] = 0;
sem_post(&sem2);
if(strncmp(buff, "end", 3) == 0)
{
break;
}
}
pthread_exit(NULL);
}
4.3条件变量
条件变量提供了一种线程间的通知机制:当某个共享数据达到某个值的时候,唤醒等待这个共享数据的线程。
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 <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
pthread_mutex_t mutex;
pthread_cond_t cond;
void * fun1(void * arg)
{
char* s = (char*)arg;
while( 1 )
{
//阻塞,被唤醒
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond,&mutex);
pthread_mutex_unlock(&mutex);
printf("fun1 read:%s\n",s);
if (strncmp(s,"end",3) == 0 )
{
break;
}
}
}
void * fun2(void * arg)
{
char* s = (char*)arg;
while( 1 )
{
//阻塞,被唤醒
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond,&mutex);
pthread_mutex_unlock(&mutex);
printf("fun2 read:%s\n",s);
if ( strncmp(s,"end",3) == 0 )
{
break;
}
}
}
int main()
{
pthread_t id[2];
char buff[128] = {0};
pthread_cond_init(&cond,NULL);
pthread_mutex_init(&mutex,NULL);
pthread_create(&id[0],NULL,fun1,(void*)buff);
pthread_create(&id[1],NULL,fun2,(void*)buff);
while( 1 )
{
fgets(buff,128,stdin);
if ( strncmp(buff,"end",3) == 0 )
{
pthread_mutex_lock(&mutex);
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
break;
}
else
{
pthread_mutex_lock(&mutex);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
}
pthread_join(id[0],NULL);
pthread_join(id[1],NULL);
exit(0);
}
4.4读写锁
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);
在读多写少的场景中,比互斥锁性能好
5.线程安全
线程安全即就是:在多线程运行的时候,不论线程的调度顺序怎样,最终的结果都是一样的、正确的。那么就说这些线程是安全的。
要保证线程安全需要做到:
1) 对线程同步,保证同一时刻只有一个线程访问临界资源。
2) 在多线程中使用线程安全的函数(可重入函数),所谓线程安全的函数指的是:如果一个函数能被多个线程同时调用且不发生竟态条件,则我们程它是线程安全的。
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
void* PthreadFun(void *arg)
{
char buff[] = "a b c d e f g h i";
char *q = NULL;
char *p = strtok_r(buff, " ", &q);//线程安全
while(p != NULL)
{
printf("fun:: %c\n", *p);
p = strtok_r(NULL, " ", &q);
sleep(1);
}
}
int main()
{
pthread_t id;
int res = pthread_create(&id, NULL, PthreadFun, NULL);
assert(res == 0);
char buff[] = "1 2 3 4 5 6 7 8 9";
char *q = NULL;
char *p = strtok_r(buff, " ", &q);
while(p != NULL)
{
printf("main:: %c\n", *p);
p = strtok_r(NULL, " ", &q);
sleep(1);
}
}
多线程中执行fork,整个进程都会被复制,但是只启用fork所在的那条执行路径
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
void fun1(void *arg)
{
fork();
for(int i-0;i<5;i++)
{
printf("");
}
}
int mian()
{
}
加锁
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include<sys/wait.h>
pthread_mutex_t mutex;
void *fun(void* ard)
{
pthread_mutex_lock(&mutex);
printf("fun lock\n");
sleep(5);//仅显示其持锁,使人肉眼观察到
pthread_mutex_unlock(&mutex);
printf("fun unlock\n");
}
int main()
{
pthread_nutex_init(&mutex,NULL);
pthread_t id;
pthread_create(&id,NULL,fun,NULL);
sleep(1);
pid_t pid=fork();
if(pid==-1)
{
exit(0);
}
if(pid==0)
{
printf("child will lock\n");
pthread_mutex_lock(&mutex);
printf("child lock\n");
pthread_mutex_unlock(&mutex);
printf("child unlock\n");
exit(0);
}
wait(NULL);
printf("main end\n");
exit(0);
}
该程序会出问题,fork()不仅将锁复制过去了,也将锁的状态也复制了,所以有可能程序运行一段时间后崩溃
解决办法:就是在没有人使用锁的时候,在使用fork()
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include<sys/wait.h>
pthread_mutex_t mutex;
void after_fork(void)
{
pthread_mutex_unlock(&mutex);
}
void before_fork(void)
{
pthread_mutex_lock(&mutex);
}
void *fun(void* ard)
{
pthread_mutex_lock(&mutex);
printf("fun lock\n");
sleep(5);//仅显示其持锁,使人肉眼观察到
pthread_mutex_unlock(&mutex);
printf("fun unlock\n");
}
int main()
{
pthread_mutex_init(&mutex,NULL);
pthread_mutex_atfork(before_fork,after_fork,before_fork);//在fork执行的时候,会自动加入
pthread_t id;
pthread_create(&id,NULL,fun,NULL);
sleep(1);
lock();//q
pid_t pid=fork();
if(pid==-1)
{
exit(0);
}
if(pid==0)
{
printf("child will lock\n");
pthread_mutex_lock(&mutex);
printf("child lock\n");
pthread_mutex_unlock(&mutex);
printf("child unlock\n");
exit(0);
}
wait(NULL);
printf("main end\n");
exit(0);
}