--线程---

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时需要加库

图片16

​ 多线程有多种执行结果,不能只看见一种场景就认为一定那一种就是正确的.

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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值