纯新多线程学习基础

程序员第二节:多线程

第二节:多线程的定义及使用



前言

昨日烟雨淅沥,往事浮沉,如今悄然无声。


提示:以下是本篇文章正文内容,下面案例可供参考

一、什么是多线程?

定义

  多线程通俗来讲既是在一个进程中的多个执行路线,她是进程内部的一个控制序列,且所有进程中至少有一个执行线路。

  多线程有哪些优点:
1:创建一个线程不需要分配单独的空间。
2:多线程之间由于是共享一块地址空间,所以线程之间方便通讯,可以做到数据共用。
3:线程之间的切换时间远远小于进程之间的切换。
4:将耗时操作置于某个线程,可以提高应用程序响应。
5:使cpu系统更加有效:当线程小于cpu总数时,不同的线程运行于不同的CPU上。
6:改善程序结构:复杂的进程分为多个线程,有利于程序的理解和修改

二、使用步骤

1.引入库

  在LINUX下编写程序,需要链接使用库libpthread.a,链接方法如下。
  由于pthrea的库不是linux的系统库,编译的时候需要加上 -lpthread:

#indclude <pthread.h>//库

host# vim hello.c//写一个线程程序
host# gcc hello.c -lpthread;//编译指令
host# ./a.out//执行程序

2.创建一个线程

#include <stdio.h>
#include <pthread.h>
void *thread_fun(void *arg)
{
	printf("this is a thread_fun\n");
	return 0;
}
int main()
{
	pthread_t rid;
	int reselt;
	//pthread_create(线程唯一表示符,线程属性,函数,函数的参数);成功返回0;
	if((reselt=pthread_create(&rid,NULL,thread_fun,NULL))!=0)
		return 0;
	printf("%d\n",reselt);//线程创建成功这里reselt=0;
	return 1;
}

  我们来看一下运行结果

在这里插入图片描述

  函数thread_fun并没有打印,但返回值为0,说明线程创建成功了,这是怎么回事呢?让我们改变一下代码:

#include <stdio.h>
#include <pthread.h>
void *thread_fun(void *arg)
{
	printf("this is a thread_fun\n");
	return 0;
}
int main()
{
	pthread_t rid;
	int reselt;
	if((reselt=pthread_create(&rid,NULL,thread_fun,NULL))!=0)
		return 0;
	printf("%d\n",reselt);//线程创建成功这里reselt=0;
	while(1);//主线程不退出
	return 1;
}

在这里插入图片描述

  当我们给主函数(主线程)加上一个循环等待,让他不退出,我们发现子线程终于执行了打印
  这说明,子线程会随着主线程的退出而退出。

3:线程传参

   pthread_create()函数的最后一个参数为要传入的参数,类型是指针,子线程中用void *类型的指针接收,所以我们需要进行指针的强制转换。

#include <stdio.h>
#include <pthread.h>

void *thread_fun(void *arg)
{
        printf("this is a thread_fun:%d\n",*(int *)arg);
        *(int *)arg=90;
        return 0;
}
int main()
{
        pthread_t tid;
        int a=3;
        int reselt=pthread_create(&tid,NULL,thread_fun,&a);
        while(a==3);
        printf("%d\n",a);
        while(1);
        return 1;
}

运行结果:this is a thread_fun=3;90;

   可以看出,子线程可以接收并更改子线程的数据。

4:线程的正常退出方式

   1:使用return;返回
   2:使用pthread_exit();


void *thread_fun(void *arg)
{
        printf("this is a thread_fun:%d\n",*(int *)arg);
        pthread_exit((void *)0);//正常退出线程
}

5:线程的返回

   函数原型:int pthread_join(pthread_t tid,void **pva)
   作用:通俗来讲就是等待另一个线程的结束,比如主线程中使用了该该函数,那么直到被等待子线程退出主线程才会退出。
   pva:返回值指针
   成功返回0,失败返回错误代码。
   当函数返回时,被等待线程的资源被收回。如果线程已经结束,该函数立即返回。并且thread指定的程序必须是joinable的。

#include <stdio.h>
#include <pthread.h>
void *thread_fun(void *arg)
{
        printf("this is a thread_fun:%d\n",*(int *)arg);
        *(int *)arg=90;
        return 0;
}
int main()
{
        pthread_t tid;
        void *userp;//返回值指针
        int a=3;
        int reselt=pthread_create(&tid,NULL,thread_fun,&a);
        pthread_join(tid,uesrP);//代替了之前的while(1)后结果依然相同
        printf("%d\n",a);
        return 1;
}

在这里插入图片描述
可以看出,主线程子线程一同正常退出

6:线程的取消

   函数原型:int pthread_cancel(pthread_t tid)
   这个函数比较好理解,该语句通过传入参数:线程ID来取消该线程的运行

7:线程间的共享资源保护

   一:互斥锁(互斥量)MUtex

   通俗理解:如果共享空间内有一段连续的内存空间,现在A线程要对其进行操作,A线程在操作的过程中释放了连续内存中的某一段。于此同时,B线程也找到了该连续的内存空间,也要对其操作,但B线程并不知道线程A已经释放了其中一个空间,从而,线程B在操作到这块空间的时候,就会产生很严重的程序错误,所以此时产生了互斥量的概念,他本质上就像一把锁,保护共享资源。
   操作步骤:

   初始化 : pthread_mutex_init
   上锁:pthread_mutex_lock
   判断上锁:pthread_mutex_unlock
   解锁:pthread_mutex_unlock
   消除互斥锁:pthread_mutex_destory

//相关函数原型
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexatter_t *attr)//mutex:创建成功后的互斥量 attr创建属性(NULL)
int pthread_mutex_destroy(pthread_mutex_t *mutex)
int pthread_mutex_lock(pthread_mutex_t *mutex)//阻塞模式,等待
int pthread_mutex_try(pthread_mutex_t*mutex)//非阻塞模式,有锁直接返回错误
int pthread_mutex_unlock(pthread_mutex_t *mutex)//在操作完成后必须对互斥量进行解锁,否则会造成死锁


   下面看一组简单的代码演示

#include <stdio.h>
#include <pthread.h>
pthread_mutex_t mutex;
int mutex_return=0;
void *thread_fun(void *arg)
{
        mutex_return=pthread_mutex_lock(&mutex);//线程1加锁
        printf("fun=%d/n",mutex_return);
        printf("this is a thread_fun:%d\n",*(int *)arg);
        *(int *)arg=90;//对共享内存进行处理
        pthread_mutex_unlock(&mutex);
        return 0;
}
void *thread_fun2(void *arg)
{
        mutex_return=pthread_mutex_lock(&mutex);//线程2加锁
        *(int *)arg=50;//对共享内存进行处理
        printf("this is a fun2\n");
        pthread_mutex_unlock(&mutex);
        pthread_exit((void *)0);
}
int main()
{
        pthread_t tid,tid2;
        void *userP;
        int a=3;
        mutex_return=pthread_mutex_init(&mutex,NULL);
        printf("mutex_return=%d\n",mutex_return);
        int reselt=pthread_create(&tid,NULL,thread_fun,&a);
        int reselt2=pthread_create(&tid2,NULL,thread_fun2,&a);
        pthread_join(tid,userP);
        pthread_join(tid2,userP);
        printf("%d\n",a);
        pthread_mutex_destroy(&mutex);//释放互斥量
        return 1;
}

  上面的程序为:在主程序中创建了两个线程,在线程中修改a的值,pthread_mutex_lock的阻塞特性,一个先上锁的线程会先执行,而后上锁的线程会处于等待状态,直到先上锁的线程解锁为止。

   二:信号量semaphore

   信号量和互斥量的区别
   1:互斥量上锁后只能由上锁的线程解锁释放,信号量可以由任何线程释放
   2:信号量的初始值可能为0或者1,而互斥量的信号量为1
   3:互斥量只有一次,而信号量相当于一个计数器,可以进行累积操作。
   信号量的操作
   每一次调用wait将会使sem-1,直到sem<0,wait阻塞(p操作)
   每一次调用post会使sem+1(V操作)
   sem<0;则sem的绝对值表示系统中等待该类临界资源线程的个数,就是等待处理资源线程的个数。
   相关函数

#include <semaphore.h>//头文件包含

int sem_init(sem_t*sem,int pshared,unsigned int value);//pshared:控制信号量的类型,0表示当前进程的局部信号量,否则在多个简称间共享  value :初始化值
int set_wait(sem_t*sem)//减  阻塞型
int set_trywait(sem_t*sem)//直接退出型

int sem_post(sem_t*sem)//加

int sem_getvalue(sem_t *sem,int *val)//得到信号量的值
int sem_destroy(sem_t*sem)//删除信号量

   下面是代码演示

#include <stdio.h>
#include <semaphore.h>
#include <pthread.h>
sem_t sem;
void *thread_fun(void *arg)
{
        int x=*(int *)arg;
        while(x)
        {
                printf("this is a good boy==%d\n",x);
                x=*(int *)arg;
                sem_wait(&sem);//对sem做减法直到<0阻塞
        }
        return 0;
}
void *thread_fun2(void *arg)
{
        while(1)
        {
                scanf("%d",(int *)arg);
                sem_post(&sem);//对sem做加法
                if(*(int *)arg==100)
                            {
                        pthread_exit(NULL);
                }

        }
        pthread_exit((void *)0);
}
int main()
{
        pthread_t tid,tid2;
        void *userP;
        int a=3;
        sem_init(&sem,1,3);
        int reselt=pthread_create(&tid,NULL,thread_fun,&a);
        int reselt2=pthread_create(&tid2,NULL,thread_fun2,&a);
        printf("one %d\n",a);
        pthread_join(tid,userP);
        pthread_join(tid2,userP);
        sem_destroy(&sem);
        printf("%d\n",a);
        return 1;
}                      

  上面的程序为:在主程序中创建了两个线程,线程A内部是一个循环函数,每次循环都对sem值减1,(初始化在主线程中,value=sem=3)当打印四次sem<0的时候,线程A开始阻塞。等待被释放或者等待其他线程执行sem_post 操作,线程B是一个循环函数,每次循环需要输从键盘上输入一个数字,然后执行sem_post让sem加1,运行结果如下图。 4为线程B的键盘输入,长语句为线程A的打印。

在这里插入图片描述

其他文章链接

上一章:纯新多进程基础篇

加入我们:QQ群:928357277

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

亿只萌新

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值