文件IO(十六)(上)

简介: 文件IO(十六)(上)

一 线程


线程的概念:线程是进程的进一步抽象。进程包括两个集合。一个资源集合,一个线程的集合,进程是资源分配的最小单位,线程是系统调度的最小单位。每个进程中必然包含一个线程,那么这个线程被称作主线程。线程的本质是一个在运行的线程函数。线程也属于并发,也会拥有自己的资源:如:Pc程序计数器,时间片,堆,栈,线程号等。线程本身并不会去申请资源,而是共享进程的资源。线程也被称为轻量级的进程,线程间共享全局变量,使得通信时会很方便。


线程不属于标准C库函数,也不属于系统调用,而是属于第三方库,libpthread.so


安装库:sudo apt-get install manpages-posix manpages-posix-dev


1.1 线程基本使用


线程的接口函数:


1.pthread_create


头文件:#include 
原型:int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                          void *(*start_routine) (void *), void *arg);
功能:创建一个线程
参数:
    thread:线程号
    attr:线程的属性和设置堆栈的大小
        分离属性:线程结束后,自己回归空间
        非分离属性:线程结束后,由创建者手动去归还空间
        一般填写NULL,一般在后面函数中设置
    start_routine:函数指针,指向了一个返回值是void*,参数是void*的函数
    arg:给线程函数传参使用,一般用于线程间通信使用,如果不传参,填写NULL
返回值:
    如果成功返回 0
    如果错误返回错误码 strerror();


注意:

1.在线程运行过程中,进程结束时资源释放后,线程没有办法执行了。

2.在多线程中,线程中尽量不调用影响整个进程的函数接口,线程本身安全星星不高,一个线程崩溃会造成进程的崩溃。

3.线程中通信的方式2种:1 全局变量 2.参数arg

注意点:如果一个线程对其进行改变,所有线程访问的值都是改变后的值。


#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
//int a = 100;
void *MyFun(void * arg)
{
    int *p = (int *)arg;
    //int a = *(int *)arg;  //线程传参
    printf("我是第一个线程\n");
    printf("子线程获取A的值为%d\n",*p);
    //a = 200;
    *p = 200;
}
int main(int argc, char const *argv[])
{
    int a = 100;
    pthread_t tid = 0;
    if(pthread_create(&tid,NULL,MyFun,&a) != 0)
    {
        printf("线程创建失败!\n");
        return -1;
    }
    printf("线程号为%ld\n",tid);
    printf("主线程获取A的值为%d\n",a);
    sleep(1);
    printf("主线程获取A的值为%d\n",a);
    return 0;
}


2.pthread_exit


头文件:#include 
原型:void pthread_exit(void *retval);
功能:结束一个线程
参数:retval:线程结束时的状态,如果不传递填写NULL
返回值:无


3.pthread_join


头文件:#include 
原型:int pthread_join(pthread_t thread, void **retval);
功能:阻塞等待回收指定线程
参数:thread:目标线程号
      retval:线程结束时,exit返回的状态
返回值:成功返回0
        失败返回错误码


#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
//int a = 100;
void *MyFun(void * arg)
{
    int *p = (int *)arg;
    //int a = *(int *)arg;  //线程传参
    printf("我是第一个线程\n");
    sleep(3);
    printf("子线程获取A的值为%d\n",*p);
    //a = 200;
    *p = 200;
    pthread_exit(NULL);
}
int main(int argc, char const *argv[])
{
    int a = 100;
    pthread_t tid = 0;
    if(pthread_create(&tid,NULL,MyFun,&a) != 0)
    {
        printf("线程创建失败!\n");
        return -1;
    }
    printf("线程号为%ld\n",tid);
    printf("主线程获取A的值为%d\n",a);
    if(0 != pthread_join(tid,NULL))
    {
        printf("回收资源失败!\n");
        return -1;
    }
    //printf("主线程获取A的值为%d\n",a);
    printf("回收资源成功!\n");
    return 0;
}


4.pthread_detach


头文件:#include 
原型:int pthread_detach(pthread_t thread);
功能:设置线程分离属性
参数:thread:目标线程号
返回值:成功返回0
        失败返回错误码
注意:设置完分离属性后,线程自动归回资源,无需join回收,join会失败


#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
//int a = 100;
void *MyFun(void * arg)
{
    int *p = (int *)arg;
    //int a = *(int *)arg;  //线程传参
    printf("我是第一个线程\n");
    sleep(3);
    printf("子线程获取A的值为%d\n",*p);
    //a = 200;
    *p = 200;
    pthread_exit(NULL);
}
int main(int argc, char const *argv[])
{
    int a = 100;
    pthread_t tid = 0;
    if(pthread_create(&tid,NULL,MyFun,&a) != 0)
    {
        printf("线程创建失败!\n");
        return -1;
    }
    if(0 != pthread_detach(tid))
    {
        printf("设置分离属性失败\n");
        return -1;
    }
    printf("线程号为%ld\n",tid);
    printf("主线程获取A的值为%d\n",a);
    if(0 != pthread_join(tid,NULL))
    {
        printf("回收资源失败!\n");
        return -1;
    }
    //printf("主线程获取A的值为%d\n",a);
    printf("回收资源成功!\n");
    return 0;
}


5. pthread_self


原型:pthread_t pthread_self(void)
功能:获取自己的线程号
参数:无
返回值:成功会返回一个线程ID号
6.pthread_cancel
原型:int pthread_cancel(pthread_t thread);
功能:申请结束一个线程
参数:目标结束的线程号
返回值:成功返回0,失败返回非0


#include <stdio.h>
#include <pthread.h>
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
//int a = 100;
void *MyFun(void * arg)
{
    int *p = (int *)arg;
    int n = 3;
    //int a = *(int *)arg;  //线程传参
    sleep(3);
    printf("我是第一个线程,我的线程ID是%ld\n",pthread_self());  
    printf("子线程获取A的值为%d\n",*p);
    //a = 200;
    *p = 200;
    pthread_exit(NULL);
}
int main(int argc, char const *argv[])
{
    int a = 100;
    pthread_t tid = 0;
    if(pthread_create(&tid,NULL,MyFun,&a) != 0)
    {
        printf("线程创建失败!\n");
        return -1;
    }
    if(0 != pthread_detach(tid))
    {
        printf("设置分离属性失败\n");
        return -1;
    }
    pthread_cancel(tid);  //发送一个申请取消线程的信号
    printf("线程号为%ld\n",tid);
    printf("主线程获取A的值为%d\n",a);
    //printf("主线程获取A的值为%d\n",a);
    printf("回收资源成功!\n");
    sleep(5);
    return 0;
}


练习3


创建两个线程,一个线程对全局变量count循环相加,每一秒加一次,另一个线程循环打印count的值,每一秒打印一次。


#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
int count = 0;
int flag = 0;
void * myfun1(void *arg)
{
    while(1)
    {
        if(flag == 0)
        {
            count++;
            //sleep(1);
            flag = 1;
        }
    }
}
int main(int argc, char const *argv[])
{
    pthread_t tid = 0;
    if(0 != pthread_create(&tid,NULL,myfun1,NULL))
    {
        perror("pthread_create");
        return -1;
    }
    while(1)
    {
        if(flag == 1)
        {
            printf("count = %d\n",count);
            flag = 0;
        }
    }
    return 0;
}


1.2 线程同步和互斥


1.2.1同步概念


什么是同步:线程间也存在着竞态的关系,对有些任务是不友好的,所以说线程提供了可以实现让多个线程同步(按一定顺序去执行)。


1.3 无名信号量:又名信号灯


本质是一个全局特殊变量,这个值不允许小于0,


1.3.1 无名信号灯的接口


sem_init
头文件:#include 
原型:int sem_init(sem_t *sem, int pshared, unsigned int value);
功能:初始化信号灯
参数:sem:信号灯变量的指针
    pshared:如果是0,代表线程间使用
            如果是非0,代表亲缘间进程使用
    value:信号的初值
返回值:
    成功返回0
    失败返回 -1.
sem_wait
头文件:#include 
原型:int sem_wait(sem_t *sem);
功能:申请资源 ----又名p操作, 也就是信号灯-1操作
参数:sem:目标信号灯
返回值:
    成功返回0
    失败返回 -1.

3. sem_post

头文件:#include 
原型:int sem_post(sem_t *sem);
功能:释放资源----又名V操作  也就是信号灯 +1操作
    V操作可以多次执行,但是我i们一般把信号量控制在0和1
参数:sem:目标信号灯
返回值:
    成功返回0
    失败返回 -1.
sem_destory
头文件:#include 
原型:int sem_destroy(sem_t *sem);
功能:销毁一个信号灯
参数:sem:目标信号灯
返回值:
    成功返回0
    失败返回 -1.


#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <semaphore.h>
int count = 0;
//定义无名信号灯
sem_t sem1;    //给线程1使用,初值应该为0
sem_t sem2;   //给线程2使用,初值应该为1
void * myfun1(void *arg)
{
    while(1)
    {
       //申请资源向信号sem1
       sem_wait(&sem1);
       count++;
       sem_post(&sem2);
    }
}
int main(int argc, char const *argv[])
{
    //初始化无名信号灯,分别初始化值为 1 0
    sem_init(&sem1,0,1);
    sem_init(&sem2,0,0);
    //开始创建线程
    pthread_t tid = 0;
    if(0 != pthread_create(&tid,NULL,myfun1,NULL))
    {
        perror("pthread_create");
        return -1;
    }
    while(1)
    {
        sem_wait(&sem2);
        printf("count = %d\n",count);
        sleep(1);
        sem_post(&sem1);
    }
    return 0;
}


相关文章
|
2月前
|
Java Unix Go
【Java】(8)Stream流、文件File相关操作,IO的含义与运用
Java 为 I/O 提供了强大的而灵活的支持,使其更广泛地应用到文件传输和网络编程中。!但本节讲述最基本的和流与 I/O 相关的功能。我们将通过一个个例子来学习这些功能。
194 1
|
6月前
|
XML JSON Go
Go语言中的文件与IO:JSON、CSV、XML处理
本文介绍了 Go 语言中对 JSON、CSV 和 XML 三种常见数据格式的处理方法。通过标准库 `encoding/json`、`encoding/csv` 和 `encoding/xml`,可以实现结构体与数据格式之间的序列化与反序列化。JSON 适合 Web API 和前后端通信,因其清晰易读;CSV 适用于表格数据和轻量级交换;XML 则支持复杂嵌套结构,常用于配置文件和 SOAP 协议。文中提供代码示例,涵盖基本使用、嵌套结构处理及实战建议,帮助开发者高效操作这些格式。
|
6月前
|
Unix Go
Go语言中的文件与IO:文件读写
本文介绍了 Go 语言中文件操作的基础方法,涵盖打开与关闭文件、读取和写入文件内容、追加写入以及复制文件等功能。通过 `os`、`bufio` 和 `io` 等标准库包,提供了高效且灵活的实现方式,如使用 `os.ReadFile` 读取整个文件、`bufio.Scanner` 逐行读取、`os.Create` 创建文件以及 `io.Copy` 复制文件内容。同时强调了错误处理的重要性,例如使用 `defer` 确保文件关闭,并推荐注意文件权限设置(如 UNIX 系统中的 `0644`)。最后以表格形式总结了常用操作及其推荐方法,便于快速查阅和应用。
|
6月前
|
Go 数据处理
Go语言中的文件与IO:bufio 和 scanner
Go 标准库中的 `bufio` 包高效读写功能,适用于文件和数据处理。`bufio.Reader` 支持按行或分隔符读取,`bufio.Writer` 提供高性能写入并需调用 `Flush()` 确保数据写入。`bufio.Scanner` 是处理文本文件(如日志、配置)的利器,可按行、单词等分割内容。本文详解其用法,并给出实践建议,如统计字符数、模拟 `tail -f` 和词频分析等。
|
10月前
|
存储 网络协议 Linux
【Linux】进程IO|系统调用|open|write|文件描述符fd|封装|理解一切皆文件
本文详细介绍了Linux中的进程IO与系统调用,包括 `open`、`write`、`read`和 `close`函数及其用法,解释了文件描述符(fd)的概念,并深入探讨了Linux中的“一切皆文件”思想。这种设计极大地简化了系统编程,使得处理不同类型的IO设备变得更加一致和简单。通过本文的学习,您应该能够更好地理解和应用Linux中的进程IO操作,提高系统编程的效率和能力。
444 34
|
Java 测试技术 Maven
Maven clean 提示文件 java.io.IOException
在使用Maven进行项目打包时,遇到了`Failed to delete`错误,尝试手动删除目标文件也失败,提示`java.io.IOException`。经过分析,发现问题是由于`sys-info.log`文件被其他进程占用。解决方法是关闭IDEA和相关Java进程,清理隐藏的Java进程后重新尝试Maven clean操作。最终问题得以解决。总结:遇到此类问题时,可以通过任务管理器清理相关进程或重启电脑来解决。
|
12月前
|
存储 Java API
【JavaEE】——文件IO(万字长文)
文件路径,文本文件,二进制文件,File类,文件流,字节流(InputStream,OutputStream)字符流(Reader,Writer)
|
搜索推荐 索引
【文件IO】实现:查找文件并删除、文件复制、递归遍历目录查找文件
【文件IO】实现:查找文件并删除、文件复制、递归遍历目录查找文件
188 2
|
编解码 Java 程序员
【文件IO】文件内容操作
【文件IO】文件内容操作
199 2
|
存储 Java API
【文件IO】文件系统操作
【文件IO】文件系统操作
155 1