【线程】多线程编程

article/2025/9/16 5:57:09

目录

一、概念

1.进程与线程的区别是?(常问)

2.线程与fork系统调用

3.线程的优缺点

4.线程的实现方式

二、线程函数

1.pthread_create

2.pthread_exit

3.pthread_join 

4.pthread_cancel

三、线程的使用

1.线程的基本操作

2.并发执行与并行执行


一、概念

  • 线程是程序中完成一个独立任务的完整执行序列,即一个可调度的实体。
  • 根据运行环境和调度者的身份,县城可分为内核线程和用户线程。
  • 线程运行在内核空间,由内核来调度。
  • 当进程的一个内核线程获得CPU的使用权时,他就加载并执行一个用户线程
  • 线程库负责管理所有执行线程,比如线程的优先级、时间片等。线程库利用longjmp来切换线程的执行,使他们看起来像”并发“执行,但实际内核仍然是把整个进程作为最小单位来调度。
  • 一个进程的所有执行线程共享该进程的时间片,他们对外表现出相同优先级。

1.进程与线程的区别是?(常问)

  • 进程:一个正在运行的程序 ,程序执行完,系统进行回收
  • 线程:进程内部的一条执行路径(序列)
  • 不同语言不同平台上线程的实现机制有所不同

2.线程与fork系统调用

  • fork创建出该进程的一份新副本。这个新进程拥有变量自己的PID,它的时间调度也是独立的,他的执行(通常)几乎完全独立于父进程。
  • 进程创建一个新线程时,新的执行线程将拥有自己的栈(因为也有自己的局部变量),但与它的创建者共享全局变量、文件描述符、信号处理函数和当前目录状态。

3.线程的优缺点

优点:

  1. 让程序看起来好像是在同时做两件事是很有用的。多任务工作如果用多进程的方式来完成很难做到高效,因为各个不同的进程必须紧密合作才能满足加锁和数据一致性方面的要求,而用多线程来完成就比多进程容易得多。
  2. 一个混杂着输入、计算和输出的应用程序,可以将这几个部分分离为3个线程来完成,从而优化程序执行的性能。
  3. 一般而言,线程之间的切换需要操作系统做的工作要比进程之间的切换少得多,因此多个线程多资源的需求要远小于多个进程。

缺点:

  1. 编写多线程程序需要非常仔细的设计。在多线程程序中,因时序上细微的偏差或无意造成的变量共享而引发错误的可能性是很大的。
  2. 对多线程程序的调试要比单个线程程序的调试困难得多,因为线程之间的交互难以控制。
  3. 将大量计算分为两个部分,并把这个两个部分作为不同的线程来运行的程序在一台单处理器机器上并不一定运行得更快,除非计算确实允许他的不同部分可以被同时计算,而且运行它的机器拥有多个处理器核来支持真正的多处理。

4.线程的实现方式

分为三种模式:完全在用户空间实现、完全由内核调度和双层调度。

用户级、内核级、组合/混合

用户:用户空间代码管理线程的创建、调度。

        优点:创建开销小,可以创建多个。

        缺点:内核感知不到用户级,内核无法将多个处理器提供给用户使用。即用户级无法使用多个处理器

内核:能够被调度到不同处理器。

组合:介于两者之间。

二、线程函数

头文件:

#include<pthread.h>

1.pthread_create

int pthread_create(pthread_t* thread,const pthread_attr_t* attr,void* (*start_routine)(void*),void* arg);
  • 作用:创建一个线程
  • 参数:
    • 第一个参数thread是新线程的标识符,后续pthread_*函数通过它来引用新进程。其类型的pthread_t定义为:
      • #include<bits/pthreadtypes.h>
        typedef unsigned long pthread_t;
      • 其中可见,pthread_t是一个整型类型。
    • 第二个参数attr用于设置新线程的属性。传递NULL表示使用默认线程属性
    • 第三个参数是返回值、参数变量都为void*的函数指针
    • 第四个参数arg表示新线程的参数
  • 返回值:成功时返回0,失败时返回错误码

2.pthread_exit

        线程一旦被创建好,内核就可以调度内核线程来执行start_rountine函数指针所指向的函数。线程函数在结束时最好调用如下函数:

void pthread_exit(void* retval);

该函数通过retval参数向进程的回收者传递其退出信息。

3.pthread_join 

int pthread_join(pthread_t thread,void**retval);
  • 作用:来回收其他线程(前提是目标线程可回收),即等待其他线程结束,类似于wait的使用
    • 一个进程中的所有线程都可以调用pthread_join函数
    • 该函数会一直被阻塞,直到被回收的线程结束为止。
  • 参数:
    • thread是目标现成的标识符
    • retval是目标线程返回的退出信息
  • 返回值:成功0,失败返回错误码

4.pthread_cancel

int pthread_cannel(pthread_t thread);
  • 作用:终止一个线程,即取消线程
  • 参数:
    • thread是目标线程标识符
  • 返回值:成功0,失败返回错误码

三、线程的使用

1.线程的基本操作

通过下面代码理解,首先主函数内执行一个主线程,fun函数执行一个线程,两线程一起执行:

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>void* fun(void* arg)
{for(int i=0;i<5;i++){printf("fun run\n");sleep(1);}
}int main()
{pthread_t id;pthread_create(&id,NULL,fun,NULL);for(int i=0;i<2;i++){printf("main run\n");sleep(1);}exit(0);
}

编译执行时,如果不在执行文件后面加上指定库名''-lpthread'',就会出现如下错误:

 执行结果:

执行结果可以得到一个结论:
主函数先结束,线程也跟着结束(fun只输出了4次)线程先结束,主函数不会结束。所以,为了使主函数”活到“最后,需要添加pthread_join函数阻塞。再添加pthread_exit对main函数返回一个值:

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>void* fun(void* arg)
{for(int i=0;i<5;i++){printf("fun run\n");sleep(1);}pthread_exit("fun over\n");
}int main()
{pthread_t id;pthread_create(&id,NULL,fun,NULL);for(int i=0;i<2;i++){printf("main run\n");sleep(1);}char* s=NULL;pthread_join(id,(void**)&s);printf("s=%s",s);exit(0);
}

执行结果如下,主函数先结束,然后阻塞住,知道线程也执行结束(输出5次),然后线程结束后向主函数发送一个“fun over”传递其退出信息。

2.并发执行与并行执行

并发与并行:

并发:一个处理器,在一段时间内交替运行

并行:任意时刻,两条程序都在执行(多个处理器)

并行是一种特殊的并发执行。

并行执行

通过创建5个线程,每个线程打印出自己是第几个创建的来观察并行执行的执行结果

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>void* fun(void* arg)
{int index=*(int*)arg;for(int i=0;i<3;i++){printf("index=%d\n",index);sleep(1);}}int main()
{pthread_t id[5];int i;for(i=0;i<5;i++){pthread_create(&id[i],NULL,fun,(void*)&i);}	for(i=0;i<5;i++){pthread_join(id[i],NULL);}exit(0);
}

每一次的执行结果都会有差异,甚至不是连续01234的数字输出,是因为并发执行是对资源共同操作,会有短暂时间内时序的影响。就比如当一个线程还在执行index++=2时,还是执行出来index=2的结果,另一个线程对index=1的值进行执行。

例题理解并发与并行差异:

通过创建5个线程,每个线程循环1000次++操作,最后打印出i的值,来观察并发执行的执行结果

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>int m_val=1;
void* fun(void* arg)
{for(int i=0;i<1000;i++){printf("m_val=%d\n",m_val++);}
}int main()
{pthread_t id[5];int i;for(i=0;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,如下图所示: 

 执行结果如下,并发执行的结果每次都一样,最终输出值为5个进程,每次打印1000次++的结果,如下图所示,每次打印的最终值都会是5000


http://chatgpt.dhexx.cn/article/REOB3hQw.shtml

相关文章

多线程编程及线程间通信机制

对进程线程的印象还是比较好的,这对于学习C高级的朋友是非常重要的,怎样更快的学习到线程的具体使用呢?最好的办法自然是练习再练习,然后还要看很多的代码才是,之前又听说了一个IT同行过劳死,特别的提醒广大的IT从业者,要注意合理的作息习惯,健康才是最重要的,下面结合…

多线程编程

多线程指的是一个程序中包含两个或者两个以上的线程&#xff0c;多线程的提出是为提高代码的执行效率&#xff0c;这就好比工厂中的流水线&#xff0c;只有一条称为单线程&#xff0c;有多条流水线就称为多线程。多线程提高效率的同时由于并发执行的不确定性&#xff0c;导致出…

两种 C++ 多线程编程方式,看完不懂打我...

多线程在实际编程中的重要性不言而喻&#xff0c;对 C 而言&#xff0c;当我们需要使用多线程时&#xff0c;有多种方案可供选择。比如 POSIX 线程 pthread、boost::thread 库、C11 开始支持的 std::thread 库&#xff0c;以及其他一些第三方库 libdispatch&#xff08;GCD&…

ARP攻击模拟工具

以下是本人平时的一些小作品&#xff0c;特此和大家一起分享... ARP攻击模拟工具 第一代 说明&#xff1a; 实现模拟以太网的ARP欺骗攻击和IP地址冲突攻击。 此为本人早期作品&#xff0c;固比较简陋和不完善&#xff0c;漏洞及错误之处可能较多请多多包涵&#xff01; 开发环…

ARP断网攻击

在之前的文章当中&#xff0c;我已经向大家介绍了关于ARP的欺骗原理。再进一步&#xff0c;将其中的原理运用到实战中去&#xff0c;又会有什么效果呢&#xff1f;ARP又可以给我们带来哪些具体的作用呢&#xff1f;毕竟实践要来支撑&#xff0c;我们之前学过的ARP欺骗原理。 我…

ARP-欺骗攻击

测试环境&#xff08;kali:192.168.189.7 win7:192.168.189.6 网关:192.168.189.2&#xff09; arpspoof -i eth0 -t 192.168.189.6 192.168.189.2 &#xff08;目标地址网关地址&#xff09; 接收获取的信息 随意登陆一个网站&#xff0c;可发现用户名及密码已被获取。可以…

ARP攻击原理及解决方法,很实用

故障原因】 局域网内有人使用ARP欺骗的木马程序&#xff08;比如&#xff1a;传奇盗号的软件&#xff0c;某些传奇外挂中也被恶意加载了此程序&#xff09;。 【故障原理】 要了解故障原理&#xff0c;我们先来了解一下ARP协议。 在局域网中&#xff0c;通过ARP协议来完成I…

ARP攻击怎么解决最安全

ARP攻击是指攻击者通过伪造网络中的ARP协议数据包&#xff0c;欺骗其他计算机的网络通信&#xff0c;从而实现中间人攻击等目的。 下面是ARP攻击的解决方法&#xff1a; 静态ARP表绑定MAC地址&#xff1a;在网络管理员的控制下&#xff0c;将主机的IP地址与MAC地址进行绑定&am…

ARP攻击及原理

ARP攻击原理:但凡局域网内存在arp攻击&#xff0c;说明网络存在“中间人” 1.PC1需要跟PC2通信&#xff0c;通过ARP请求包询问PC2的MAC地址&#xff0c;由于采用广播的形式&#xff0c;所以交换机会将ARP请求包从接口PC1广播到PC2和PC3。(注&#xff1a;交换机收到广播/组播/未…

ARP攻击、欺骗及防御

一、广播与广播域概述 1、广播与广播域 广播&#xff1a;将广播地址做为目的地址的数据帧。 广播域&#xff1a;网络中能接收到同一个广播所有节点的集合。 2、MAC地址广播 广播地址为FF-FF-FF-FF-FF-FF 3、IP地址广播 1&#xff09;255.255.255.255 2&#xff09;广播…

ARP攻击原理和kali实现ARP攻击

目录 一、ARP协议和ARP攻击1.ARP协议2.利用Wireshark分析ARP数据包3.ARP攻击 二、kali实现ARP攻击和ARP欺骗1.实验过程2.问题记录T_T 一、ARP协议和ARP攻击 1.ARP协议 ARP协议&#xff0c;地址解析协议&#xff08;Address Resolution Protocol&#xff09;&#xff0c;用来实…

怎么处理ARP攻击

ARP故障说明: ping 的时候可能好久才能接收到一个响应&#xff0c;或者说干脆就Ping不通。网络时好时坏&#xff0c;通过ARP -a命令不能看到同网段的在线用户&#xff0c;但是net view 可以看到全网开放共享的电脑名&#xff0c;这时候就需要注意了&#xff0c;可能是ARP攻击。…

网络安全--ARP攻击原理与防护

目录 一.ARP的原理 二.ARP攻击现象及危害 三.ARP攻击的原理 四.ARP防护 在局域网当中&#xff0c;有一个协议由于它的特性一旦遭受攻击就非常麻烦。首先是它的攻击门槛比较低&#xff0c;找到一些小工具就能实现攻击&#xff0c;而且危害极大。这个协议就是ARP协议。 ARP攻击是…

渗透技术——ARP攻击

Part 1: ARP攻击介绍 ARP&#xff08;Address Resolution Protocol&#xff0c;地址解析协议&#xff09;是一个位于TCP/IP协议栈中的底层协议&#xff0c;负责将某个IP地址解析成对应的MAC地址。而ARP攻击就是通过伪造IP地址和MAC地址实现ARP欺骗&#xff0c;能够在网络中产生…

Linux操作系统及其发行版本

文章目录 Linux是什么Liunx与WindowsLinux的发行版本1. Debian系列&#xff1a;2. Slackware系列&#xff1a;3. Redhat系列&#xff1a;4. 其他发行版本&#xff1a; Linux是什么 Linux是一套免费使用和自由传播的类Unix操作系统&#xff0c;是一个基于POSIX和UNIX的多用户、…

linux查看系统版本命令

*一、查看Linux系统发行版本 * 命令1&#xff1a;lsb_release -a 该命令适用于所有Linux系统&#xff0c;会显示出完整的版本信息&#xff0c;包括Linux系统的名称&#xff0c;如Debian、Ubuntu、CentOS等&#xff0c;和对应的版本号&#xff0c;以及该版本的代号&#xff0c…

哪些是linux系统版本,linux系统主要有哪些版本

linux系统主要版本有&#xff1a;Redhat版本&#xff0c;基于RPM包的YUM包管理方式&#xff1b;2.CentOS版本&#xff0c;免费的、开源的、可以重新分发的linux发行版&#xff1b;3.Ubuntu版本&#xff0c;拥有漂亮的用户界面的系统&#xff1b;4.Mandriva版本&#xff0c;KDE桌…

Linux操作系统介绍及版本

1.1 认识Linux Linux操作系统是基于UNIX以网络为核心的设计思想&#xff0c;是一个性能稳定的多用户网络操作系统&#xff0c;Linux能运行各种工具软件、应用程序以及网络协议&#xff0c;它支持安装在32位和64位CPU硬件上。 通常来讲&#xff0c;Linux这个词只表示Linux内核&a…

Linux版本简介

Linux系统最早由LinusTorvalds在1991年开始编写&#xff0c;在此诞生之前同时也具备了五大前提条件&#xff1a;UNIX操作系统、MINIX 操作系统、GNU计划、POSIX标准、INTERNET。先让我们看一下Linux系统的发行版本&#xff0c;包括Redhat、Ubuntu、Fedora、SUSE、Slackware、De…

Linux各个系统版本及区别(Ubuntu,CentOS..)

一、概览 Linux的版本号分为两部分&#xff1a;内核版本和发行版本。 内核版本&#xff1a;不包含外围程序GNU程序库和工具&#xff0c;命令行shell&#xff0c;图形界面的X Window系统和相应的桌面环境等 发行版本&#xff1a;RedHatLinux&#xff0c;CentOS, Ubuntu&#xff…