【Linux-day11-线程的创建与同步】

本文详细介绍了Linux中线程的概念,进程与线程的区别,以及三种线程实现方式。重点讲解了Linux如何将线程视为进程实现,提供了pthread_create,pthread_exit,pthread_join等接口,并通过示例展示了线程同步技术,如信号量和互斥锁的使用。

Linux 线程的创建与同步

线程的概念

线程是进程内部的一条执行序列或执行路径,一个进程可以包含多条线程。

进程与线程的区别
  • 进程是资源分配的最小单位,线程是 CPU 调度的最小单位
  • 进程有自己的独立地址空间,线程共享进程中的地址空间
  • 进程的创建消耗资源大,线程的创建相对较小
  • 进程的切换开销大,线程的切换开销相对较小
线程的实现方式

在操作系统中,线程的实现有以下三种方式:

  • 内核级线程
  • 用户级线程
  • 组合级线程

Linux 中线程的实现

Linux 实现线程的机制非常独特。从内核的角度来说,它并没有线程这个概念。Linux 把
所有的线程都当做进程来实现。内核并没有准备特别的调度算法或是定义特别的数据结构来
表征线程。相反,线程仅仅被视为一个与其他进程共享某些资源的进程。每个线程都拥有唯
一隶属于自己的 task_struct,所以在内核中,它看起来就像是一个普通的进程(只是线程和
其他一些进程共享某些资源,如地址空间)。

线程库中的接口介绍
pthread_create()用于创建线程

int pthread_create(pthread_t *thread, **const ** pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

成功返回 0, 失败返回错误码

thread: 接收创建的线程的 ID

attr: 指定线程的属性

start_routine: 指定线程函数

arg: 给线程函数传递的参数

pthread_exit()退出线程

int pthread_exit(void *retval);

pthread_exit()退出线程

retval:指定退出信息

pthread_join()等待 thread 指定的线程退出,线程未退出时,该方法阻塞

int pthread_join(pthread_t thread, void **retval);

retval:接收 thread 线程退出时,指定的退出信息

测试代码1
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>

void* fun(void* arg)
{
    int* p = (int*)arg;
    int val = *p;
        printf("%d\n",val);
    return NULL;
}

int main()
{
    pthread_t id[5];
    int i=0;
    for(;i<5;i++)
    {
        pthread_create(&id[i],NULL,fun,(void*)&i);
    }
    for(i=0;i<5;i++)
    {
        pthread_join(id[i],NULL);
    }
    exit(0);
}

预估:会出现0 1 2 3 4按一定顺序

结果如图:

我们发现结果和预想的不一样,这是因为线程是并发运行的,主线程main()和5个副线程都在同时运行,fun是通过指针解引用,主线程会随时改变i的值 ; pthread_create(&id[i],NULL,fun,(void*)&i);只是向内核申请,不一定顺序批准。

测试代码2
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
int val = 0;
void* fun(void* arg)
{
    for(int i=0;i<1000;i++)
    {
        printf("%d\n",++val);
    }
    return NULL;
}
int main()
{
    pthread_t id[5];
    int i=0;
    for(;i<5;i++)
    {
        pthread_create(&id[i],NULL,fun,(void*)&i);
    }
    for(i=0;i<5;i++)
    {
        pthread_join(id[i],NULL);
    }
    exit(0);
}

预期: 输出5000

结果如图:

这是因为线程是并发执行的,上一个线程对val进行++完,还没有写回内存,下个进程读取了之前的值对其++;

线程同步

1. 信号量

函数介绍

int sem_init(sem_t * sem, int pshared,unsigned int value)

sem: 指向的信号量对象

pshared: 0表示此信号为当前进程局部的,否则为多个进程间共享

value: 设置信号的值

int sem_wait(sem_t * sem)

进行p操作,信号值减1

sem: 指向的信号量对象

int sem_post(sem_t * sem)

进行v操作,信号值加1

sem: 指向的信号量对象

int sem_destory(sem_t * sem)

j清理该信号拥有的所有资源,成功返回0

sem: 指向的信号量对象

下面使用该方法对测试代码2修改

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<semaphore.h>

int val = 0;
sem_t sig;
void* fun(void* arg)
{
    for(int i=0;i<1000;i++)
    {
        sem_wait(&sig);
        printf("%d\n",++val);
        sem_post(&sig);
    }
    return NULL;
}
int main()
{
    pthread_t id[5];
    sem_init(&sig,0,1);
    int i=0;
    for(;i<5;i++)
    {
        pthread_create(&id[i],NULL,fun,(void*)&i);
    }
    for(i=0;i<5;i++)
    {
        pthread_join(id[i],NULL);
    }
    sem_destroy(&sig);
    exit(0);
}

结果如图:

2.互斥锁

接口介绍:

1.int pthread_mutex_init(pthread_mutex_t* mutex,const pthread_mutexattr_t * mutexattr);

初始化锁
mutex: 互斥变量的指针
mutexattr: 设置互斥锁的属性

2.int pthread_mutex_lock(pthread_mutex_t* mutex);

上锁(加锁)

3.int pthread_mutex_unlock(pthread_mutex_t* mutex);

解锁

4.int pthread_mutex_destroy(pthread_mutex_t* mutex);

销毁锁

使用此方法修改测试代码2

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<semaphore.h>


int val = 0;
pthread_mutex_t mutex;
void* fun(void* arg)
{
    for(int i=0;i<1000;i++)
    {
        pthread_mutex_lock(&mutex);
        printf("%d\n",++val);
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

int main()
{
    pthread_t id[5];
    pthread_mutex_init(&mutex,NULL);
    int i=0;
    for(;i<5;i++)
    {
        pthread_create(&id[i],NULL,fun,(void*)&i);
    }
    for(i=0;i<5;i++)
    {
        pthread_join(id[i],NULL);
    }
    pthread_mutex_destroy(&mutex);
    exit(0);

}

结果如图:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值