S5.3:线程基础

这篇博客详细介绍了Linux环境下多线程的概念和使用,包括进程与线程的特点、线程的创建、回收、同步和互斥机制。通过实例展示了如何使用pthread库创建和管理线程,以及如何使用信号量和互斥锁实现线程间的同步和互斥,防止数据竞争问题。
摘要由CSDN通过智能技术生成

一.线程的概念和使用

线程的概念

  • 共享相同地址空间的多个任务

1.进程相关特点

  1. 进程有独立的地址空间(父子空间也独立)
  2. Linux为每个进程创建task_struct
  3. 每个进程都参与内部调度,互不影响

2.线程相关特点

  1. 很多操作系统引入轻量级进程LWP
  2. 同一进程中的线程共享相同的地址空间
  3. Linux不区分线程,进程
  • 使用多线程可以避免额外的TLB&cache的刷新

3.共享数据

  1. 可执行指令
  2. 静态数据(全局变量)
  3. 工作目录
  4. 用户ID&用户组ID

4.私有数据

  1. 线程ID
  2. PC(程序计数器)&寄存器
  3. 堆栈
  4. 错误号
  5. 优先级
  6. 执行状态和属性

线程库pathread

1.创建线程

  • 任一线程执行exit会导致整个进程结束
  • 查看线程命令(ps -eLf|grep cthread)
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*routine)(void *), void *arg);
/*
thread 线程ID
attr 线程属性,NULL表示默认属性
routine 线程执行的函数
arg 传递给routine的参数,
创建成功返回0,失败返货错误码(无法被perror显示)(使用strerror)
*/
  • 创建线程案例
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

void *function1(void *args);
void *function2(void *args);

int main(int argc, char const *argv[])
{
    pthread_t tid;
    int err;

    err = pthread_create(&tid, NULL, function1, NULL);
    if (err != 0)
    {
        printf("create thread:%s", strerror(err));
    }
    sleep(1);
    err = pthread_create(&tid, NULL, function2, NULL);
    if (err != 0)
    {
        printf("create thread:%s", strerror(err));
    }

    sleep(1);
    return 0;
}

void *function1(void *args)
{
    printf("this is thread function1\n");
}

void *function2(void *args)
{
    printf("this is thread function2\n");
}
代码编译问题及解决(转载)

问题描述

  • undefined reference to ‘pthread_create’
    undefined reference to ‘pthread_join’
    问题原因:
    pthread 库不是 Linux 系统默认的库,连接时需要使用静态库 libpthread.a,所以在使用pthread_create()创建线程,以及调用 pthread_atfork()函数建立fork处理程序时,需要链接该库。

问题解决

  • 在编译中要加 -lpthread参数
    gcc thread.c -o thread -lpthread
    thread.c为你些的源文件,不要忘了加上头文件#include<pthread.h>
    ————————————————
    版权声明:本文为CSDN博主「李刘强博客」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/llqkk/article/details/2854558

2.回收线程

  • 防止线程变成僵尸线程
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
//void **retval为pthread_exit退出函数的返回值
  • 线程分离,线程主动与主控线程断开关系,运行结束后自动释放所有资源,防止产生僵尸线程
int pthread_detach(pthread_t thread);
//成功返回0,失败返回错误码;
  • 回收线程案例

3.结束线程

  • 结束当前线程
  • retval可被其他线程通过pthread_join获取
  • 线程私有资源被释放
#include <pthread.h>
void pthread_exit(void *retval);
  • 结束&回收线程案例
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <pthread.h>
#include <unistd.h>
#include <fcntl.h>

void *function1(void *args);
void *function2(void *args);

int main(int argc, char const *argv[])
{
    pthread_t tid;
    int err;
    void *retval;

    err = pthread_create(&tid, NULL, function1, NULL);
    if (err != 0)
    {
        printf("create thread:%s", strerror(err));
    }
    sleep(1);
    err = pthread_join(tid, &retval); //回收线程
    //printf("retval:%s\n",(char *)retval);
    printf("retval:%d\n",(int)retval);

    return 0;
}

void *function1(void *args)
{
    int ret=5;
    printf("this is function thread\n");
    sleep(1);
    //pthread_exit("function exit");
    pthread_exit((void *)ret);
}

4.取消线程

  • 发一个信号给线程使其停止
int pthread_cancel(pthread_t thread);
  • 当某些属性被设置后线程不会停止
int pthread_setcancelstate(int state,int *oldstate);//是否允许取消
PTHREAD_CANCEL_ENABLE
PTHREAD_CANCEL_DISABLE

//线程只有在看守点才能取消
int pthread_setcanceltype(int state,int *oldstate);//是否延时取消
void pthread_testcancel(void);//使程序运行到看守点
PTHREAD_CANCEL_DEFERRED//使程序运行到看守点
PTHREAD_CANCEL_ASYNCHRONOUS

5.同步和互斥机制

同步机制
  • 线程共享同一地址空间
  • 线程之间通信容易,可通过全局变量交换数据
  • 多个线程访问共享数据时需要同步或互斥机制

同步

  • 多个任务按照约定的先后顺序执行

信号量(灯)

  • p:如果信号量大于0,申请资源任务继续执行,信号量减一,不大于0,任务阻塞
  • v:如果有任务在等待资源,唤醒等待的任务,使其继续运行

posix信号量

  • 无名信号量(基于内存的信号量)
  • 有名信号量

pthread库

int sem_init(sem_t *sem, int pshared, unsigned int value);
//信号量参数|0/1|信号量初值
//0线程间通信,1进程间通信
int sem_wait(sem_t *sem);//p操作
int sem_post(sem_t *sem);//v操作

读写线程案例

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

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


void *write1(void *args);
void *read1(void *args);
char buffer[100];
sem_t sem;//信号量数据结构


int main(int argc, char const *argv[])
{
    pthread_t tid1,tid2;
    int err,i=0;
    void *retval;
    sem_init(&sem,0,0);

    err = pthread_create(&tid1, NULL, write1, (void *)i);
    if (err != 0)
    {
        printf("create thread:%s", strerror(err));
    }
    sleep(1);
    err = pthread_join(tid1, &retval); //回收线程
    

    err = pthread_create(&tid2, NULL, read1, (void *)i);
    if (err != 0)
    {
        printf("create thread:%s", strerror(err));
    }
    sleep(1);
    err = pthread_join(tid2, &retval); //回收线程
    while (1)
    {
        sleep(1);
    }
    
    return 0;
}

void *write1(void *args)
{
    int a = 0;
    a = (int)args;
    int td = pthread_self();
    pthread_detach(pthread_self());
    while (1)
    {
        fgets(buffer,20,stdin);
        sem_post(&sem);//写线程,写完后释放资源,使用v操作
    }
}

void *read1(void *args)
{
    int a = 0;
    a = (int)args;
    int td = pthread_self();
    pthread_detach(pthread_self());
    while (1)
    {
        memset(buffer,0,100);
        sem_wait(&sem);//度线程,等待写线程的资源,p操作]
        printf("%s\n",buffer);
    }
}
错误及解决方案
 error: conflicting types for ‘write’
 情况1:未实现声明函数
 情况2:函数名和头文件中已定义的函数名同名
 解决方案:修改函数名
互斥机制

临界资源

  • 一次只允许一个任务(进程,线程)访问的共享资源

临界区

  • 访问临界区的代码

互斥机制

  • mutex互斥锁
  • 任务访问临界资源前申请互斥锁,然后释放锁

互斥锁

  1. 初始化互斥锁
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr);
  • 成功返回0,失败返回错误码
  • mutex是指向要初始化的互斥锁对象
  • attr互斥锁属性,NULL表示属性缺失
  1. 申请锁
int pthread_mutex_lock(pthead_mutex_t *mutex);
  • 成功返回0,失败返回错误码
  • 如果无法获取锁,任务阻塞
  1. 释放锁
int pthread_mutex_unlock(pthead_mutex_t *mutex);
  • 成功返回0,失败返回错误码
  • 执行完临界区要及时释放
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <pthread.h>
#include <unistd.h>
#include <fcntl.h>
#include <semaphore.h>

void *function1(void *args);
void *function2(void *args);
FILE *fp;
pthread_mutex_t mutex;

int main(int argc, char const *argv[])
{
    pthread_t tid, tid1;
    int err,i=0;

    fp = fopen("1.txt", "w");
    if (!fp)
    {
        perror("fopen1");
        return -1;
    }

    pthread_mutex_init(&mutex, NULL);

    err = pthread_create(&tid, NULL, function1,(void *)i);
    err = pthread_detach(tid); //回收线程
    if (err != 0)
    {
        printf("create thread:%s", strerror(err));
        exit(0);
    }

    err = pthread_create(&tid1, NULL, function2, (void *)i);
    err = pthread_detach(tid1);
    if (err != 0)
    {
        printf("create thread:%s", strerror(err));
        exit(0);
    }
    
    while (1)
    {
        sleep(1);
    }

}

void *function1(void *args)
{

    int a = 0;
    a = (int)args;

    char *c1 = "HELLO WORLD\n";
    char *c2;
    int len = strlen(c1);

    int td = pthread_self();
    pthread_detach(pthread_self());
    c2 = c1;
    while (1)
    {
        pthread_mutex_lock(&mutex);
        for (int i = 0; i < len; i++)
        {
            fputc(*c1, fp);
            fflush(fp);
            c1++;
            usleep(10000);
        }
        pthread_mutex_unlock(&mutex);
        c1 = c2;
        sleep(1);
    }
}

void *function2(void *args)
{
    int a = 0;
    a = (int)args;

    char *c1 = "HOW ARE YOU\n";
    char *c2;
    int len = strlen(c1);

    int td = pthread_self();
    pthread_detach(pthread_self());
    c2 = c1;
    while (1)
    {
        pthread_mutex_lock(&mutex);
        for (int i = 0; i < len; i++)
        {
            fputc(*c1, fp);
            fflush(fp);
            c1++;
            usleep(10000);
        }
        pthread_mutex_unlock(&mutex);
        c1 = c2;
        sleep(1);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值