首页 > 系统相关 >Linux基础实训(利用线程和互斥锁)

Linux基础实训(利用线程和互斥锁)

时间:2023-02-17 15:33:45浏览次数:53  
标签:thread int cond 互斥 mutex 实训 pthread 线程


                     实验要求(linux)
1 定义一个长度为8的数组
2 输入不同的8个(大写)字母(ASDFGHJK)
3 对字符串进行排序

4 用线程和互斥锁输出按顺序输出AAAAAAADDDDDDDD........SSSSSSSS.
提示:

for(...A..H)
{
...
pthread.create(function,arg) ;
}
/*pthread.c*/
#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<unistd.h>
#include"sort.h"
#define MAX 20
int n ; // 全局变量 数字的个数
pthread_mutex_t mutex ; // 互斥区
void *thread(char *str)
{
char s = *(char*) str ;
pthread_mutex_lock(&mutex) ;
for(int i = 0 ; i<n; i++ )
printf("%c",s) ;
pthread_mutex_unlock(&mutex) ;
return NULL ;
}
int main()
{
pthread_t id[10] ;
int res ;
char buf[10] ;
int len ;
pthread_mutex_init(&mutex,NULL) ; // 初始化互斥
printf("Please input 8 letters : \n ") ;
scanf("%s",buf) ;
len = strlen(buf) ;
n = len ;
bubble_sort(buf,len) ;
printf("%s\n" ,buf) ; // 输出排好序的
for(int i = 0 ; i<len ; i++ )
{
res = pthread_create(&id[i], NULL ,(void *)thread ,&buf[i]);
if(res!=0)
{
printf("Creat thread error \n" ) ;
return -1 ;
}

pthread_join(id[i],NULL) ;//调用线程阻塞

}
pthread_mutex_destroy(&mutex) ; // 离开互相斥区

return 0 ;
}
#include "sort.h"
void bubble_sort(char str[] ,int n )
{
char tmp ;
for(int i = 0 ; i <n-1 ; i++)
{
for(int j = 0 ;j<n-i-1 ; j++)
{
if(str[j] > str[j+1])
{
tmp = str[j] ;
str[j] = str[j+1] ;
str[j+1] = tmp ;
}


}
}
return ;
}

 

#ifndef _SORT_H
#define _SORT_H
void bubble_sort(char a[],int n ) ;
#endif
#makefile
pthread: pthread.o sort.o
gcc -o pthread pthread.o sort.o -lpthread
pthread.o: pthread.c sort.h
gcc -c pthread.c
sort.o: sort.c sort.h
gcc -c sort.c
clean:
rm *.o

 

Linux多线程编程(一)---多线程基本编程

    线程是指运行中的程序的调度单位。一个线程指的是进程中一个单一顺序的控制流,也被称为轻量级线程。它是系统独立调度和分配的基本单位。同一进程中的多个线程将共享该系统中的全部系统资源,比如文件描述符和信号处理等。一个进程可以有很多线程,每个线程并行执行不同的任务。

线程与进程比较

   ①  和进程相比,它是一种非常“节俭”的多任务操作方式。在Linux系统中,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护其代码段、堆栈段和数据段,这种多任务工作方式的代价非常“昂贵”。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且线程间彼此切换所需要时间也远远小于进程间切换所需要的时间。

   ②  线程间方便的通信机制。对不同进程来说它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行。这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其他线程所用,不仅方便,而且快捷。

线程基本编程

   Linux系统下的多线程遵循POSIX线程接口,称为pthread。编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a。因为pthread的库不是Linux系统的库,所以在编译时要加上 -lpthread。例如:gcc  filename  -lpthread。注意,这里要讲的线程相关操作都是用户空间中的线程的操作。

   线程创建:创建线程实际上就是确定调用该线程函数的入口点,这里通常使用的函数是pthread_create()。在线程创建后,就开始运行相关的线程函数。


Linux基础实训(利用线程和互斥锁)_初始化

  线程退出:在线程创建后,就开始运行相关的线程函数,在该函数运行完之后,该线程也就退出了,这也是线程退出的一种方法。另一种退出线程的方法是使用函数pthread_exit(),这是线程的主动行为。这里要注意的是,在使用线程函数时,不能随意使用exit()退出函数来进行出错处理。由于exit()的作用是使调用进程终止,而一个进程往往包含多个线程,因此,在使用exit()之后,该进程中的所有线程都终止了。在线程中就可以使用pthread_exit()来代替进程中的exit()。


Linux基础实训(利用线程和互斥锁)_初始化_02

  线程等待:由于一个进程中的多个线程是共享数据段的,因此,通常在线程退出后,退出线程所占用的资源并不会随着线程的终止而得到释放。正如进程之间可以用wait()系统调用来同步终止并释放资源一样,线程之间也有类似机制,那就是pthread_join()函数。pthread_join()用于将当前进程挂起来等待线程的结束。这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源就被收回。


Linux基础实训(利用线程和互斥锁)_信号量_03

  线程取消:前面已经提到线程调用pthread_exit()函数主动终止自身线程,但是在很多线程应用中,经常会遇到在别的线程中要终止另一个线程的问题,此时调用pthread_cancel()函数来实现这种功能,但在被取消的线程的内部需要调用pthread_setcancel()函数和pthread_setcanceltype()函数设置自己的取消状态。例如,被取消的线程接收到另一个线程的取消请求之后,是接受函数忽略这个请求;如果是接受,则再判断立刻采取终止操作还是等待某个函数的调用等。


Linux基础实训(利用线程和互斥锁)_信号量_04

  线程标识符获取:获取调用线程的标识ID。


Linux基础实训(利用线程和互斥锁)_初始化_05

  线程清除:线程终止有两种情况:正常终止和非正常终止。线程主动调用pthread_exit()或者从线程函数中return都将使线程正常退出,这是可预见的退出方式;非正常终止是线程在其它线程的干预下,或者由于自身运行出错(比如访问非法地址)而退出,这种退出方式是不可预见的。不论是可预见的线程终止还是异常终止,都回存在资源释放的问题,如何保证线程终止时能顺利地释放掉自己所占用的资源,是一个必须考虑的问题。

  从pthread_cleanup_push()的调用点到pthread_cleanup_pop()之间的程序段中的终止动作(包括调用pthread_exit()和异常终止,不包括return)都将执行pthread_cleanup_push()所指定的清理函数。


Linux基础实训(利用线程和互斥锁)_信号量_06


Linux基础实训(利用线程和互斥锁)_初始化_07

实验1

   功能:使用pthread_create()函数创建线程的实例

   代码:thread_create.c文件


Linux基础实训(利用线程和互斥锁)_条件变量_08


Linux基础实训(利用线程和互斥锁)_初始化_09

    编译:使用命令:gcc thread_create.c -o thread_create -lpthread编译,注意不要忘了加 -lpthread,否则会出现如下的错误

/tmp/ccjfZIN3.o: In function `main':
thread_create.c:(.text+0x8b): undefined reference to `pthread_create'
thread_create.c:(.text+0xc0): undefined reference to `pthread_create'
thread_create.c:(.text+0xeb): undefined reference to `pthread_join'
thread_create.c:(.text+0xfc): undefined reference to `pthread_join'
collect2: ld returned 1 exit status

   执行:


Linux基础实训(利用线程和互斥锁)_条件变量_10

实验2

  功能:使用pthread_exit()函数退出线程的举例

  代码:thread_exit.c文件


Linux基础实训(利用线程和互斥锁)_条件变量_11

  编译:gcc thread_exit.c -o thread_exit -lpthread

  执行:./thread_exit


Linux基础实训(利用线程和互斥锁)_初始化_12

实验3

  功能:用pthread_join()实现线程等待。

  代码:thread_join.c文件


Linux基础实训(利用线程和互斥锁)_信号量_13

  编译:gcc thread_join.c -o thread_join -lpthread  

  执行:./thread_join


Linux基础实训(利用线程和互斥锁)_信号量_14

 可以看出,pthread_join()等到线程结束后,程序才继续执行。

实验4

  功能:使用pthread_self()获取线程ID

  代码:thread_id.c文件


Linux基础实训(利用线程和互斥锁)_信号量_15

  编译:gcc thread_id.c -o thread_id -lpthread

  执行:./thread_id


Linux基础实训(利用线程和互斥锁)_信号量_16

实验5

  功能:线程清理函数的使用

  代码:thread_clean.c


Linux基础实训(利用线程和互斥锁)_信号量_17


Linux基础实训(利用线程和互斥锁)_信号量_18


Linux基础实训(利用线程和互斥锁)_条件变量_19

   注意,在编写的代码的时候,自己修改一下传递的参数和clean_pop函数的参数,相信你会更有收获。

  编译:gcc thread_clean.c -o thread_clean -lpthread

  执行:./thread_clean


Linux基础实训(利用线程和互斥锁)_初始化_20

实验6

  功能:本实验创建了3个进程,为了更好的描述线程之间的并行执行,让3个线程共用同一个执行函数。每个线程都有5次循环(可以看成5个小任务),每次循环之间会随机等待1~10s的时间,意义在于模拟每个任务的到达时间是随机的,并没有任何特定的规律。

  代码:thread.c文件,该代码文件​​点此下载​​


Linux基础实训(利用线程和互斥锁)_初始化_21


Linux基础实训(利用线程和互斥锁)_条件变量_22

  编译:gcc thread.c -o thread -lpthread

  执行:./thread


Linux基础实训(利用线程和互斥锁)_条件变量_23


  从实验结果可以看出,线程执行的顺序杂乱无章的,看着就头疼,下一节就利用线程之间的同步与互斥机制处理此文件

 

线程的最大特点是资源的共享性,但资源共享中的同步问题是多线程编程的难点。linux下提供了多种方式来处理线程同步,最常用的是互斥锁、条件变量和信号量。

一、互斥锁(mutex)

通过锁机制实现线程间的同步。

  • 初始化锁。在Linux下,线程的互斥量数据类型是pthread_mutex_t。在使用前,要对它进行初始化。
    静态分配:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    动态分配:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_attr_t *mutexattr);
  • 加锁。对共享资源的访问,要对互斥量进行加锁,如果互斥量已经上了锁,调用线程会阻塞,直到互斥量被解锁。
    int pthread_mutex_lock(pthread_mutex *mutex);
    int pthread_mutex_trylock(pthread_mutex_t *mutex);
  • 解锁。在完成了对共享资源的访问后,要对互斥量进行解锁。
    int pthread_mutex_unlock(pthread_mutex_t *mutex);
  • 销毁锁。锁在是使用完成后,需要进行销毁以释放资源。
    int pthread_mutex_destroy(pthread_mutex *mutex);

 

 

Linux基础实训(利用线程和互斥锁)_信号量_24

Linux基础实训(利用线程和互斥锁)_信号量_25

Linux基础实训(利用线程和互斥锁)_信号量_26

Linux基础实训(利用线程和互斥锁)_初始化_27

 

 

二、条件变量(cond)

互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。条件变量分为两部分: 条件和变量。条件本身是由互斥量保护的。线程在改变条件状态前先要锁住互斥量。条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。条件的检测是在互斥锁的保护下进行的。如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量可以被用来实现这两进程间的线程同步。

  • 初始化条件变量。
    静态态初始化,pthread_cond_t cond = PTHREAD_COND_INITIALIER;
    动态初始化,int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
  • 等待条件成立。释放锁,同时阻塞等待条件变量为真才行。timewait()设置等待时间,仍未signal,返回ETIMEOUT(加锁保证只有一个线程wait)
    int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
    int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,const timespec *abstime);
  • 激活条件变量。pthread_cond_signal,pthread_cond_broadcast(激活所有等待线程)
    int pthread_cond_signal(pthread_cond_t *cond);
    int pthread_cond_broadcast(pthread_cond_t *cond); //解除所有线程的阻塞
  • 清除条件变量。无线程等待,否则返回EBUSY
    int pthread_cond_destroy(pthread_cond_t *cond);

 

 

 

 

 

三、信号量(sem)

如同进程一样,线程也可以通过信号量来实现通信,虽然是轻量级的。信号量函数的名字都以"sem_"打头。线程使用的基本信号量函数有四个。

  • 信号量初始化。
    int sem_init (sem_t *sem , int pshared, unsigned int value);
    这是对由sem指定的信号量进行初始化,设置好它的共享选项(linux 只支持为0,即表示它是当前进程的局部信号量),然后给它一个初始值VALUE。
  • 等待信号量。给信号量减1,然后等待直到信号量的值大于0。
    int sem_wait(sem_t *sem);
  • 释放信号量。信号量值加1。并通知其他等待线程。
    int sem_post(sem_t *sem);
  • 销毁信号量。我们用完信号量后都它进行清理。归还占有的一切资源。
    int sem_destroy(sem_t *sem);

 

 

 

 

标签:thread,int,cond,互斥,mutex,实训,pthread,线程
From: https://blog.51cto.com/u_15970235/6064290

相关文章

  • JS为什么是单线程
    JS为什么是单线程进程与线程什么是进程都知道,CPU是计算机的核心,承担所有的计算任务官网说法,进程是CPU资源分配的最小单位字面意思就是进行中的程序,可以将它理解为......
  • 谈JVM参数GC线程数ParallelGCThreads合理性设置
    作者:京东零售刘乐1.ParallelGCThreads参数含义在讲这个参数之前,先谈谈JVM垃圾回收(GC)算法的两个优化标的:吞吐量和停顿时长。JVM会使用特定的GC收集线程,当GC开始的时候,GC......
  • 线程停止
          ......
  • Java线程基础
    线程的创建Java线程可以通过实现Runnable接口、Callable接口或继承Thread类进行创建。实现Runnable接口实例可以使用Runnable接口实现多线程,Runnable接口实现类必须实现......
  • 期末复习——同步、互斥、死锁
    不同进程之间的关系同步=直接制约关系,相互之间协调顺序,进行阻塞等调整。互斥=间接制约关系,一个进程进入临界区,另一进程必须等待。同步临界区问题多道程序环......
  • 多线程对拍
    #include<bits/stdc++.h>usingnamespacestd;constintAllThrCnt=1e4,SinThrCnt=1;autosysrun(stringstr){ returnsystem(str.c_str());}enumjudgeinfo{J......
  • 线程池总结
    一、什么是线程池,为什么需要线程池池化思想在计算机领域并不少见,在SQL连接,TCP链接中也常常见到,本质思想都是一样的,就是资源复用,线程池也差不多就是这个道理,它将多个线程资......
  • 【开源需求】C++多线程消息分发架构
    项目【gi_messager】开源项目:https://girakoo.com/联系方式:[email protected]需求简述在多线程环境中,为每个线程提供独立的消息队列MessageLoop。注:主线程默认自动......
  • 谈谈muduo库的销毁连接对象——C++程序内存管理和线程安全的极致体现
    #前言网络编程的连接断开一向比连接建立复杂的多,这一点在陈硕写的muduo库中体现的淋漓尽致,同时也充分体现了C++程序在对象生命周期管理上的复杂性,稍有不慎,满盘皆输。为......
  • 线程基本知识
    多线程基础1.线程相关概念1.1程序(program)最为简单完成特点任务,用某种语言编写的一组指令的集合.最简单的说:就是我们写的代码1.2进程1.进程是指运行中的程序,......