当前位置: 首页 > article >正文

Linux中线程创建,线程退出,线程接合

线程的简单了解

之前我们了解过 task_struct 是用于描述进程的核心数据结构。它包含了一个进程的所有重要信息,并且在进程的生命周期内保持更新。我们想要获取进程相关信息往往从这里得到。

  • 在Linux中,线程的实现方式与进程类似,每个线程都有一个task_struct结构体,用于存储线程的信息。线程的task_struct结构体比进程的task_struct结构体要小,包含的信息更少。
  • 进程是操作系统资源分配的最小单位,而线程是操作系统调度的最小单位。
  • 线程之间的切换通常比进程切换更高效,因为线程共享进程的资源,不需要像进程切换那样保存和恢复大量的资源信息。

windows中的线程和Linux中线程区别 

在Windows操作系统,内核中有真线程,名为TCB :线程控制块。需要维护进程与线程之间的调度关系算法,这过于复杂。

 在Linux中,由于线程的控制块与进程控制块相似性非常高,所以直接复用了PCB的结构体——task_struct ,用PCB模拟线程的TCB。所以Linux没有真正意义上的线程,而是用进程方案模拟的线程。这样做的好处是复用代码和结构更简单,好维护,效率更高,也更安全。

线程的特性 

多线程的优点

  • 同时执行多个任务: 多线程允许程序同时执行多个任务,而不是按顺序一个接一个地执行。
  • 提高响应速度: 对于需要处理大量并发请求的程序(如Web服务器),多线程可以显著提高程序的响应速度。
  • 更小开销,更快的切换: 线程切换的开销也比进程切换要小,这使得多线程程序可以更高效地进行任务切换。
  • 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务
  • 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现,可以有效提高计算效率,但是注意:线程不是越多越好,正常情况下最合适的原则是:进程/线程与cpu个数/核数保持一致

多线程的缺点

  • 共享资源竞争 多个线程共享进程的地址空间,当它们同时访问和修改共享资源时,可能会出现竞争条件,导致数据不一致或程序错误。
  • 同步机制复杂 为了解决线程安全问题,需要使用线程同步机制(如互斥锁、条件变量等),这些机制会增加编程的复杂性,容易出错。
  • 上下文切换开销: 线程切换需要保存和恢复线程的上下文,这会消耗一定的CPU时间。过多的线程切换可能会降低程序的效率。
  • 线程间依赖: 线程之间可能存在依赖关系,一个线程的执行可能会影响到其他线程的执行。如果处理不当,可能会导致程序出现意外错误。
  • 调试困难:多线程程序的执行顺序是不确定的,这使得程序的调试变得更加困难。由于线程的执行受到多种因素的影响,一些错误可能很难复现,增加了调试的难度。

PROSIX线程库

与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以pthread_打头的
要使用这些函数库,要通过引入头文件 <pthread.h>

并且链接这些线程函数库时要使用编译器命令的“-lpthread”选项

之前我们使用的都是linux中的基础标准库,这些标准库在编译的时候会自动帮我们进行链接,我们只需要包含一个头文件,不需要手动链接。但是对于线程库的话默认不会帮我们链接,除了需要我们程序中包含对应头文件,还需要编译的时候手动链接

包含头文件和编译时链接区分

  • 包含头文件(#include): 这是在源代码文件中做的,用于告诉编译器程序中使用了哪些函数、变量、类型等。头文件通常包含函数声明、宏定义、结构体定义等。
  • 编译时链接: 这是在编译命令中做的,用于告诉链接器将程序中使用的函数和变量与它们在库文件中的具体实现链接起来。库文件通常包含编译好的函数和变量的二进制代码。

包含头文件的作用:

  • 让编译器理解代码: 头文件相当于一个“接口说明书”,告诉编译器程序中使用了哪些“零件”(函数、变量等),以及这些“零件”的规格(参数类型、返回值类型等)。
  • 提供类型检查: 编译器可以根据头文件中的声明来检查程序中函数和变量的使用是否正确,避免类型错误。

编译时链接的作用:

  • 生成可执行文件: 链接器将程序中使用的函数和变量与它们在库文件中的实现“组装”起来,生成最终的可执行文件。
  • 链接外部代码: 程序中使用的某些函数和变量可能不是由自己编写的,而是由其他人或组织提供的,这些代码通常放在库文件中。链接器将这些外部代码链接到程序中,使得程序可以使用这些外部功能。

线程创建

pthread_create函数介绍

函数作用:创建一个新的线程。这个线程在创建后会并行执行指定的线程函数

头文件:#include <pthread.h>

函数原型

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

参数

  • thread: 一个指向 pthread_t 类型变量的指针,用于存储新创建线程的 ID
  • attr: 一个指向 pthread_attr_t 类型变量的指针,用于设置新线程的属性。如果设置为 NULL,则使用默认属性。通常我们设置成NULL就行
  • start_routine: 一个函数指针,表示新线程要执行的函数。该函数必须接受一个 void * 类型的参数,并返回一个 void * 类型的值。
  • arg: 一个指向 void * 类型变量的指针,表示传递给 start_routine 线程函数的参数。如果我们不需要传递任何数据给线程函数,完全可以将它设置为 NULL

返回值

  • 成功: 返回 0。
  • 失败: 返回一个非零的错误码,表示创建线程失败的原因。

线程函数的定义

线程函数必须符合 void *(*start_routine)(void *) 的函数签名,即接收一个 void * 类型的参数并返回一个 void * 类型的值。

简单的线程创建例子

例子比较简单,主要就是创建了一个新的线程,然后主线程和新线程同时执行,主线程输出26个英文字母,新线程输出数字0-9。

编译代码的时候记得加上编译链接选项:-lpthread

gcc a.c -o a -lpthread
#include <stdio.h>
#include <pthread.h>void *thread_function(void *arg) {int i=0;while(1){fprintf(stderr,"%d",i);i++;if(i==10){i=0;}}
}int main() {pthread_t thread_id;int ret = pthread_create(&thread_id, NULL, thread_function, NULL);if (ret != 0) {perror("pthread_create failed");return 1;}printf("Thread created successfully\n");int i=0;while(1){fprintf(stderr,"%c",'a'+i);i++;if(i==26){i=0;}}return 0;
}

最终看到的效果就是主线程和新线程双线执行输出。

线程间共享资源与不共享资源

共享资源

  • 堆(Heap): 存储进程中动态分配的对象。
  • 代码段(Code Segment): 存储程序的指令。
  • 数据段(Data Segment): 存储进程的全局变量和静态变量
  • 文件描述符表: 存储进程打开文件的信息。
  • 信号处理函数: 用于处理进程接收到的信号。

不共享资源

  • 栈(Stack): 每个线程都有自己的栈,用于存储局部变量、函数调用信息等。
  • 寄存器(Registers): 每个线程都有一组寄存器,用于存储线程执行过程中的临时数据。
  • 线程 ID: 每个线程都有一个唯一的线程 ID,用于标识线程。

使用共享资源时需要注意的问题:

  • 竞态条件(Race Condition): 多个线程同时访问和修改共享资源时,可能会导致数据不一致的问题。
  • 死锁(Deadlock): 多个线程互相等待对方释放资源,导致程序无法继续执行的问题。

解决竞态条件和死锁问题的方法:

  • 互斥锁(Mutex): 用于保护共享资源,同一时刻只允许一个线程访问。
  • 条件变量(Condition Variable): 用于线程之间的同步,当一个线程等待某个条件满足时,可以使用条件变量进行阻塞。
  • 信号量(Semaphore): 用于控制同时访问共享资源的线程数量。

多线程共享资源同时访问出错例子

场景假设有一个共享的计数器变量 counter,初始值为 0。现在有多个线程同时对 counter 进行加 1 操作。

预期结果由于有 10 个线程,每个线程执行 100000 次加 1 操作,因此最终的 counter 值应该为 10 * 100000 = 1000000。

实际结果实际运行结果通常会小于 10000。

原因分析

当多个线程同时访问 counter 变量时,由于线程切换的存在,可能会导致以下情况:

  1. 线程 A 读取 counter 的值。
  2. 线程 A 被切换出去,线程 B 开始执行。
  3. 线程 B 读取 counter 的值。
  4. 线程 B 将 counter 的值加 1。
  5. 线程 B 被切换出去,线程 A 继续执行。
  6. 线程 A 将之前读取的 counter 值加 1,并写回。

这样,线程 A 和线程 B 都只进行了一次加 1 操作,但 counter 的值只增加了 1,而不是 2。这种情况称为竞态条件

解决方法可以使用互斥锁(Mutex)来保护共享资源 counter,确保同一时刻只有一个线程可以访问它。

通过使用互斥锁,可以保证每个线程对 counter 的加 1 操作都是原子性的,从而避免竞态条件,得到正确的结果。

#include <stdio.h>
#include <pthread.h>#define NUM_THREADS 10
#define INCREMENTS 100000int counter = 0;void *increment_counter(void *arg) {for (int i = 0; i < INCREMENTS; i++) {counter++;}return NULL;
}int main() {pthread_t threads[NUM_THREADS];for (int i = 0; i < NUM_THREADS; i++) {pthread_create(&threads[i], NULL, increment_counter, NULL);}//作用是等待多个线程执行结束。它通常出现在多线程程序中,//用于确保主线程在所有子线程完成任务后才退出。for (int i = 0; i < NUM_THREADS; i++) {pthread_join(threads[i], NULL);}printf("Expected counter value: %d\n", NUM_THREADS * INCREMENTS);printf("Actual counter value: %d\n", counter);return 0;
}

线程退出 

pthread_exit 函数介绍

 函数作用:结束调用该函数的线程,同时还可以传递一个退出状态值给其他线程

头文件:#include <pthread.h>

函数原型

void pthread_exit(void *retval);

参数

retval 参数可以用于传递一个退出状态值给其他线程,通常通过 pthread_join() 函数来接收这个值。如果不需要传递退出状态,可以将 retval 设置为 NULL。

僵尸线程

首先我们来回顾一下僵尸进程:

僵尸进程

  • 当一个进程结束运行时,内核不会立即释放它占用的所有资源,而是将其状态设置为僵尸态(Zombie)
  • 僵尸进程会保留一些基本信息(如进程ID、退出状态等),以便父进程可以获取到子进程的退出信息。
  • 父进程需要调用 wait()waitpid() 等函数来回收僵尸进程的资源,否则僵尸进程会一直存在,占用系统资源。

然后我们来看一下线程的僵尸态

与进程类似,当一个线程结束运行时,它也会进入一个类似于僵尸态的状态。

  • 状态保留: 线程退出后,其占用的大部分资源(如栈空间)会被自动回收,但是线程也会保留一些状态信息,例如退出状态,以便其他线程(通常是主线程)可以通过 pthread_join() 函数来获取。
     
  • 回收方式: 线程的“回收”主要通过 pthread_join() 函数来实现。当主线程调用 pthread_join() 函数等待某个线程结束时,实际上就是在“回收”该线程的状态信息。

僵尸态的重要性 

  • 无论是进程还是线程,僵尸态的存在都是为了让父进程或主线程能够获取到子进程或子线程的退出信息
  • 这些退出信息可能包含执行结果、错误码等,对于程序的调试和错误处理非常有帮助。

线程接合

pthread_join函数介绍

 函数作用:阻塞当前线程,直到指定的线程执行完毕,适用于线程间的同步

头文件:#include <pthread.h>

函数原型

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

参数

thread:要等待的线程的线程 ID。这是一个由 pthread_create 创建的线程 ID。

retval:这是一个指向指针的指针,函数会把目标线程的退出状态通过该指针返回。如果目标线程没有返回任何值,可以传递 NULL。

返回值

  • 成功: 返回 0。
  • 失败: 返回一个非零的错误码。

适用场景

  • 同步线程: 当一个线程需要等待另一个线程完成后才能继续执行时,可以使用 pthread_join() 函数进行同步。
  • 获取线程返回值: 有些线程会返回一个值,表示它们的执行结果。可以使用 pthread_join() 函数获取这个返回值。
  • 资源回收: 当一个线程结束后,它的资源不会立即被释放。需要调用 pthread_join() 函数才能回收这些资源。

 线程分离态(了解)

线程的默认状态

默认情况下,新创建的线程都处于非分离态 (Joinable State)。这意味着:

  1. 资源回收: 当一个线程结束运行时,它所占用的资源(如栈空间)不会立即被释放,而是会保留一段时间,直到有其他线程调用 pthread_join() 函数来“回收”该线程。
  2. 获取退出状态: 其他线程可以通过调用 pthread_join() 函数来等待该线程结束,并获取它的退出状态。

什么是分离态?

分离态 (Detached State) 是一种特殊的线程状态。当一个线程被设置为分离态时,它与创建它的线程(通常是主线程)之间的关系就会被“分离”。这意味着:

  1. 自动资源回收: 当一个分离态线程结束运行时,它所占用的资源会被自动回收,无需其他线程调用 pthread_join() 函数。
  2. 无法获取退出状态: 其他线程无法通过 pthread_join() 函数来等待分离态线程的结束,也无法获取它的退出状态。

如何设置线程为分离态?

方法一

pthread_detach(thread_id);

这个函数可以直接将指定线程设置为分离状态。

但是你可能会想如何获得一个线程自身的线程tid呢?其实很简单,有一个函数pthread_self可以很容易帮我们获取到当前线程的tid。

这个函数在后面一篇文章中也会详细讲到,这里只是简单提一下。

函数原型:pthread_t pthread_self(void);

所以我们常常将 pthread_self 配合 pthread_detach 一起使用,像下面这样:

pthread_detach(pthread_self());

方法二

使用pthread_create函数创建线程的时候,有一个参数可以设置新创建的线程的属性。我们可以凭借这个参数来设置线程为分离态,这种方式相比于方法一,更加麻烦,但是有着自己的优点,之后我们会详细讲到,这里不详细阐述。

适用场景

有些情况下,我们对于某些线程来说不关心它的返回状态,并且也不想要使用pthread_join来阻塞等待回收这个死后的僵尸线程。那么此时我们就可以把这个线程设置成分离态度,当线程死亡自动释放,不需要其他线程调用pthread_join来回收这个僵尸进程的资源。

相关文章:

Linux中线程创建,线程退出,线程接合

线程的简单了解 之前我们了解过 task_struct 是用于描述进程的核心数据结构。它包含了一个进程的所有重要信息&#xff0c;并且在进程的生命周期内保持更新。我们想要获取进程相关信息往往从这里得到。 在Linux中&#xff0c;线程的实现方式与进程类似&#xff0c;每个线程都…...

Groovy语言的学习路线

Groovy语言的学习路线 引言 在当前的编程世界中&#xff0c;随着多种编程语言的涌现&#xff0c;开发者常常需要选择合适的语言来满足项目的需求。Groovy语言作为一种动态语言&#xff0c;在Java虚拟机&#xff08;JVM&#xff09;环境中得到了广泛使用。它具有简单易用的语法…...

nlf 3d pose 部署学习笔记

目录 multi_hmr创建SemanticRenderer 推理代码 渲染代码: 调用原版render,没成功 用的pose和smlx生成vertices,也有vertices3d hmr2,用的是网络生成的vertices进行渲染。 nlf地址: GitHub - isarandi/nlf: [NeurIPS 2024] Neural Localizer Fields for Continuous …...

【vmware虚拟机安装教程】

以下是在VMware Workstation Pro上安装虚拟机的详细教程&#xff1a; 准备工作 下载VMware Workstation Pro 访问VMware官网下载并安装VMware Workstation Pro&#xff08;支持Windows和Linux系统&#xff09;。安装完成后&#xff0c;确保已激活软件&#xff08;试用版或正式…...

window中git bash使用conda命令

window系统的终端cmd和linux不一样&#xff0c;运行不了.sh文件&#xff0c;为了在window中模仿linux&#xff0c;可以使用gui bash模拟linux的终端。为了在gui bash中使用python环境&#xff0c;由于python环境是在anaconda中创建的&#xff0c;所以需要在gui bash使用conda命…...

PHP语法完全入门指南:从零开始掌握动态网页

本文专为零基础新手设计,通过5000字详细讲解带你系统学习PHP语法。包含环境搭建、基础语法、实战案例,并附20+代码示例。阅读后你将能独立开发简单动态网页! 一、PHP开发环境搭建(新手必看) 1.1 为什么需要搭建环境? PHP是服务器端脚本语言,需要运行在服务器环境中。推…...

什么是蒸馏技术

蒸馏技术&#xff08;Knowledge Distillation, KD&#xff09;是一种模型压缩和知识迁移的方法&#xff0c;旨在将一个复杂模型&#xff08;通常称为“教师模型”&#xff09;的知识转移到一个小型模型&#xff08;通常称为“学生模型”&#xff09;中。蒸馏技术的核心思想是通…...

Python——寻找矩阵的【鞍点】(教师:恒风)

在矩阵中&#xff0c;一个数在所在行中是最大值&#xff0c;在所在列中是最小值&#xff0c;则被称为鞍点 恒风的编程 思路&#xff1a; 使用while循环找到行中最大值&#xff0c;此时列的坐标已知&#xff0c;利用列表推导式生成列不变的纵列&#xff0c;利用min()函数得到纵…...

处理项目中存在多个版本的jsqlparser依赖

异常提示 Correct the classpath of your application so that it contains a single, compatible version of net.sf.jsqlparser.statement.select.SelectExpressionIte实际问题 原因&#xff1a;项目中同时使用了 mybatis-plus 和 pagehelper&#xff0c;两者都用到了 jsqlpa…...

【iOS】包大小和性能稳定性优化

包大小优化 图片 LSUnusedResources 扫描重复的图片 ImageOptim,压缩图片 压缩文件 优化音视频资源 &#xff0c;使用MP3 代替 WAV ffmpeg -i input.mp3 -b:a 128k output.mp3 视频 H.265&#xff08;HEVC&#xff09; 代替 H.264 ffmpeg ffmpeg -i input.mp4 -vcodec lib…...

Delphi语言的软件工程

Delphi语言的软件工程 引言 在软件工程的历史长河中&#xff0c;Delphi语言作为一种快速应用程序开发&#xff08;RAD&#xff09;的工具&#xff0c;凭借其高效的开发环境和强大的编程能力&#xff0c;一直在软件开发领域占有一席之地。本文将探讨Delphi语言的历史背景、特性…...

Jenkinsdebug:遇到ERROR: unable to select packages:怎么处理

报错信息&#xff1a; 报错信息解释&#xff1a; musl-1.2.5-r0 和 musl-dev-1.2.5-r1: 这里说明 musl-dev 需要一个特定版本的 musl&#xff0c;即 musl1.2.5-r1&#xff0c;但是当前版本的 musl&#xff08;1.2.5-r0&#xff09;并不满足这个条件。版本冲突: 当尝试安装新…...

3、树莓派5 安装VNC查看器 开启VNC服务器

在前序文章中&#xff08; 2、树莓派5第一次开机&#xff09;&#xff0c;可以使用三种方式开机&#xff0c;其中使用网线及wifi的方式均需要使用到VNC查看器进行远程桌面控制&#xff0c;本文将介绍如何下载安装并配置及使用VNC查看器及服务器&#xff0c;对前序文章做一些补充…...

数据结构——单向循环链表、双链表、双向循环链表

目录 一、单向循环链表 1.1 单向循环链表的概念 1.2 单向循环链表的操作 1.2.1 单向循环链表的创建 1.2.2 单向循环链表的头插 1.2.3 单向循环链表的遍历 1.2.4 单向循环链表的头删 1.2.5 单向循环链表的尾插 1.2.6 单向循环链表的尾删 1.2.7 约瑟夫环 1.3 单向循环列表所有程…...

冒险岛079 V8 整合版源码搭建教程+IDEA启动

今天教大家来部署下一款超级怀旧游戏冒险岛&#xff0c;冒险岛源码是开源的&#xff0c;但是开源的代码会有各种&#xff0c;本人进行了加工整合&#xff0c;并且用idea进行了启动测试&#xff0c;经过修改后没有任何问题。 启动截图 后端控制台 前端游戏界面 声明 冒险岛源码…...

Qwen2-VL 的重大省级,Qwen 发布新旗舰视觉语言模型 Qwen2.5-VL

Qwen2.5-VL 是 Qwen 的新旗舰视觉语言模型&#xff0c;也是上一代 Qwen2-VL 的重大飞跃。 Qwen2.5-VL主要特点 视觉理解事物&#xff1a;Qwen2.5-VL不仅能够熟练识别花、鸟、鱼、昆虫等常见物体&#xff0c;而且还能够分析图像中的文本、图表、图标、图形和布局。 代理性&…...

详解CountDownlatch

引言 CountDownLatch 是 Java 并发包 java.util.concurrent 中的一个同步工具类&#xff0c;由著名的并发编程大师 Doug Lea 所开发。它允许一个或多个线程等待其他线程完成操作后再继续执行&#xff0c;其核心思想是通过一个计数器&#xff0c;对计数器进行调度来实现 原理 …...

STM32外设SPI FLASH应用实例

STM32外设SPI FLASH应用实例 1. 前言1.1 硬件准备1.2 软件准备 2. 硬件连接3. 软件实现3.1 SPI 初始化3.2 QW128 SPI FLASH 驱动3.3 乒乓存储实现 4. 测试与验证4.1 数据备份测试4.2 数据恢复测试 5 实例5.1 参数结构体定义5.2 存储参数到 SPI FLASH5.3 从 SPI FLASH 读取参数5…...

【Excel笔记_6】条件格式和自定义格式设置表中数值超过100保留1位,超过1000保留0位,低于100为默认

方法一&#xff1a;自定义格式 选中需要设置格式的单元格区域。右键选择设置单元格格式&#xff0c;或者在工具栏中选择开始 -> 数字 -> 自定义格式。在类型框中输入以下自定义格式&#xff1a; [>1000]0;[>100]0.0;G/通用格式解释&#xff1a; [>1000]0&…...

Java零基础入门笔记:(1-2)入门(简介、基础知识)

前言 本笔记是学习狂神的java教程&#xff0c;建议配合视频&#xff0c;学习体验更佳。 【狂神说Java】Java零基础学习视频通俗易懂_哔哩哔哩_bilibili - Java简介 Java是一种广泛使用的高级编程语言&#xff0c;具有简单、面向对象、分布式、多线程、动态性、健壮性和安全…...

Unreal5从入门到精通之使用 BindWidget 将 C++ 连接到 UMG 蓝图

文章目录 前言示例代码BindWidget使用C++的优缺点可选小部件其他特性前言 如果你开始制作基于 C++ 的 UI,你最常遇到的一个问题是: 如何使用 C++ 来控制蓝图创建的小部件? 这个问题的答案是BindWidget元属性。 BindWidget 示例 UPROPERTY(BlueprintReadWrite, meta = (…...

Java 基于 SpringBoot+Vue 的动漫平台(附源码,文档)

博主介绍&#xff1a;✌程序员徐师兄、8年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战*✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447…...

微信小程序 - 网络请求基础路径集中管理(基础路径集中管理策略、动态切换基础路径)

一、基础路径集中管理 在微信小程序项目开发中&#xff0c;经常会将请求的基础路径集中管理 这样可以避免在多个页面中重复定义&#xff0c;同时也方便后续维护与修改 二、基础路径集中管理策略 1、使用全局变量 微信小程序提供了 App 对象&#xff0c;可以在 app.js 中定义…...

Ubuntu 系统 cuda12.2 安装 MMDetection3D

DataBall 助力快速掌握数据集的信息和使用方式&#xff0c;会员享有 百种数据集&#xff0c;持续增加中。 需要更多数据资源和技术解决方案&#xff0c;知识星球&#xff1a; “DataBall - X 数据球(free)” 贵在坚持&#xff01; ---------------------------------------…...

DDD该怎么去落地实现(3)通用的仓库和工厂

通用的仓库和工厂 我有一个梦&#xff0c;就是希望DDD能够成为今后软件研发的主流&#xff0c;越来越多研发团队都转型DDD&#xff0c;采用DDD的设计思想和方法&#xff0c;设计开发软件系统。这个梦想在不久的将来是有可能达成的&#xff0c;因为DDD是软件复杂性的解决之道&a…...

【SpringBoot苍穹外卖】debugDay0 打开前端页面

在某一天学完后&#xff0c;电脑关机&#xff0c;再打开啥都忘了&#xff0c;记起来一点点&#xff0c;前端页面打不开&#xff0c;后端控制台一直循环出错。原来是下面这样哈哈。 查看端口是否被别的程序占用的操作步骤 winR输入cmd打开命令行 netstat -ano | findstr "8…...

康耐视CAM-CIC-10MR-10-GC工业相机

康耐视(COGNEX)的工业相机CAM-CIC-10MR-10-GC是CAM-CIC-10MR系列中的一款型号,主要应用于工业自动化检测和高精度视觉系统 基本参数与特性 分辨率与帧率: CAM-CIC-10MR-10-GC属于康耐视CIC系列,具备10MP(1000万像素)的分辨能力,帧率为10fps。该系列相机支持卷帘快门(R…...

华为昇腾服务器部署DeepSeek模型实战

在华为的昇腾服务器上部署了DeepSeek R1的模型进行验证测试&#xff0c;记录一下相关的过程。服务器是配置了8块910B3的显卡&#xff0c;每块显卡有64GB显存&#xff0c;根据DeepSeek R1各个模型的参数计算&#xff0c;如果部署R1的Qwen 14B版本&#xff0c;需要1张显卡&#x…...

做谷歌SEO需要了解哪些基本概念?

做谷歌SEO时&#xff0c;必须掌握一些基本的概念。首先是关键词。关键词是用户在搜索框里输入的词汇&#xff0c;它们直接影响到你网站的排名。所以&#xff0c;了解用户的搜索习惯、挑选合适的关键词&#xff0c;是每一个SEO优化者必须做的工作。 内容是关键。谷歌非常看重网…...

通过BingAPI爬取Bing半个月内壁纸

通过BingAPI爬取Bing半个月内壁纸 一、前言二、爬虫代码三、代码说明 一、前言 爬取Bing搜索网站首页壁纸的方式主要有两种&#xff0c;第一种为间接爬取&#xff0c;即并不直接对Bing网站发起请求&#xff0c;而是对那些收集汇总了Bing壁纸的网站发起请求&#xff0c;爬取图片…...