Linux系统编程——线程的学习
学习参考博文:
Linux多线程编程初探
Linux系统编程学习相关博文
- Linux系统编程——文件编程的学习
- Linux系统编程——进程的学习
- Linux系统编程——进程间通信的学习
- Linux系统编程——网络编程的学习
Linux系统编程——线程的学习
- 一、概述
- 1. 进程与线程的区别
- 2. 使用线程的理由
- 3. 互斥量
- 4. 条件变量
- 二、线程API
- 三、API介绍
- 1. pthread_creat函数
- 2. pthread_exit函数
- 3. pthread_join函数
- 4. pthread_self函数
- 5. pthread_mutex_inti函数
- 6. pthread_mutex_destroy函数
- 7. pthread_mutex_lock函数
- 8. pthread_mutex_unlock函数
- 9. pthread_cond_init函数
- 10. pthread_cond_destroy函数
- 11. pthread_cond_wait函数
- 12. pthread_cond_signal函数
- 四、API的使用例子
- 1. pthread_creat、pthread_exit、pthread_join、pthread_self函数
- 2. pthread_mutex_init、pthread_mutex_destory、pthread_mutex_lock、pthread_mutex_unlock函数
- 3. pthread_cond_init、pthread_cond_destory、pthread_cond_wait、pthread_cond_signal函数
一、概述
常规学习Linux系统编程的内容是复杂且繁多的,不推荐刚开始接触代码的朋友去学习,所以介绍Linux系统编程的目的主要是以应用开发为主。
1. 进程与线程的区别
- 进程——资源分配的最小单位,线程——程序执行的最小单位
- 在面向线程设计的系统中,进程本身不是基本运行单位,而是线程的容器。程序本身只是指令、数据及其组织形式的描述,进程才是程序 (那些指令和数据) 的真正运行实例。(程序是静态的,进程是动态的)
- 一个进程里边可以有多个线程。进程的所有信息对该进程的所有线程都是共享的,包括可执行的程序文本、程序的全局内存和堆内存、栈以及文件描述符。
- 进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。
- 总的来说:进程有独立的地址空间,线程没有单独的地址空间(同一进程内的线程共享进程的地址空间)。
2. 使用线程的理由
- 使用多线程的理由之一是和进程相比,它是一种非常"节俭"的多任务操作方式。我们知道,在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。据统计,总的说来,一个进程的开销大约是一个线程开销的30倍左右,当然,在具体的系统上,这个数据可能会有较大的区别。
- 使用多线程的理由之二是线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。当然,数据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,有的子程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地方。
3. 互斥量
- 互斥量(mutex)从本质上来说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。对互斥量进行加锁后,任何其他试图再次对互斥量加锁的线程将会被阻塞直到当前线程释放该互斥锁。如果释放互斥锁时有多个线程阻塞,所有在该互斥锁上的阻塞线程都会变成可运行状态,第一个变为可运行状态的线程可以对互斥量加锁,其他线程将会看到互斥锁依然被锁住,只能回去等待它重新变为可用。在这种方式下,每次只有一个线程可以向前运行。
- 在设计时需要规定所有的线程必须遵守相同的数据访问规则。只有这样,互斥机制才能正常工作。操作系统并不会做数据访问的串行化。如果允许其中的某个线程在没有得到锁的情况下也可以访问共享资源,那么即使其它的线程在使用共享资源前都获取了锁,也还是会出现数据不一致的问题。
- 互斥变量用pthread_mutex_t数据类型表示。在使用互斥变量前必须对它进行初始化,可以把它置为常量PTHREAD_MUTEX_INITIALIZER(只对静态分配的互斥量),也可以通过调用pthread_mutex_init函数进行初始化。如果动态地分配互斥量(例如通过调用malloc函数),那么在释放内存前需要调用pthread_mutex_destroy。
4. 条件变量
- 条件变量是线程另一可用的同步机制。条件变量给多个线程提供了一个会合的场所。条件变量与互斥量一起使用时,允许线程以无竞争的方式等待特定的条件发生。
- 条件本身是由互斥量保护的。线程在改变条件状态前必须首先锁住互斥量,其他线程在获得互斥量之前不会察觉到这种改变,因为必须锁定互斥量以后才能计算条件。
- 条件变量使用之前必须首先初始化,pthread_cond_t数据类型代表的条件变量可以用两种方式进行初始化,可以把常量PTHREAD_COND_INITIALIZER赋给静态分配的条件变量,但是如果条件变量是动态分配的,可以使用pthread_cond_destroy函数对条件变量进行去除初始化(deinitialize)。
二、线程API
在Linux系统中,操作系统提供了一系列的API,详细看下图
1. 线程
创建 pthread_creat()
退出 pthread_exit()
等待 pthread_join()
获取ID pthread_self()
2. 互斥锁
创建 pthread_mutex_init()
销毁 pthread_mutex_destroy()
加锁 pthread_mutex_lock()
解锁 pthread_mutex_unlock()
3. 条件
创建 pthread_cond_init()
销毁 pthread_cond_destroy()
触发 pthread_cond_signal()
广播 pthread_cond_broadcast()
等待 pthread_cond_wait() / pthread_cond_timewait()
三、API介绍
1. pthread_creat函数
#include <pthread.h>int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);1. 函数功能:创建线程
2. 形参说明:
tidp:线程ID
attr:线程属性。暂可以把它设置为NULL,以创建默认属性的线程。
start_rtn:新创建线程的运行函数
arg:函数运行传递的参数
3. 返回值:成功返回0,失败返回错误编号
2. pthread_exit函数
#include <pthread.h>int pthread_exit(void *rval_ptr);1. 函数功能:线程退出
2. 形参说明:
rival_ptr:退出时返回的数据
3. pthread_join函数
#include <pthread.h>int pthread_join(pthread_t thread, void **rval_ptr);1. 函数功能:调用这个函数的线程将一直阻塞,直到指定的线程调用pthread_exit、从启动例程中返回或者被取消。
2. 形参说明:
thread:线程ID
rival_ptr:线程退出时返回的数据
3. 返回值:成功返回0,失败返回错误编号
4. pthread_self函数
#include <pthread.h>pthread_t pthread_self(void);1. 函数功能:获取线程ID
2. 返回值:调用线程的ID
5. pthread_mutex_inti函数
#include <pthread.h>int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);1. 函数功能:创建互斥锁
2. 形参说明:
mutex:锁ID
attr:锁的属性。默认的属性初始化互斥量,只需把attr设置为NULL。
3. 返回值:成功返回0,失败返回错误编号
6. pthread_mutex_destroy函数
#include <pthread.h>int pthread_mutex_destroy(pthread_mutex_t *mutex);1. 函数功能:销毁互斥锁
2. 形参说明:
mutex:锁ID
3. 返回值:成功返回0,失败返回错误编号
7. pthread_mutex_lock函数
#include <pthread.h>int pthread_mutex_lock(pthread_mutex_t *mutex);1. 函数功能:加锁
2. 形参说明:
mutex:锁ID
3. 返回值:成功返回0,失败返回错误编号
8. pthread_mutex_unlock函数
#include <pthread.h>int pthread_mutex_unlock(pthread_mutex_t *mutex);1. 函数功能:解锁
2. 形参说明:
mutex:锁ID
3. 返回值:成功返回0,失败返回错误编号
9. pthread_cond_init函数
#include <pthread.h>int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);1. 函数功能:创建条件变量
2. 形参说明:
cond:条件ID
attr:条件属性。除非需要创建一个非默认属性的条件变量,否则该参数可以设置为NULL。
3. 返回值:成功返回0,失败返回错误编号
10. pthread_cond_destroy函数
#include <pthread.h>int pthread_cond_destroy(pthread_cond_t *cond);1. 函数功能:销毁条件变量
2. 形参说明:
cond:条件ID
3. 返回值:成功返回0,失败返回错误编号
11. pthread_cond_wait函数
#include <pthread.h>int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);1. 函数功能:等待条件为真
2. 形参说明:
cond:条件ID
mutex:锁ID
3. 返回值:成功返回0,失败返回错误编号
12. pthread_cond_signal函数
#include <pthread.h>int pthread_cond_signal(pthread_cond_t *cond);1. 函数功能:触发条件
2. 形参说明:
cond:条件ID
3. 返回值:成功返回0,失败返回错误编号
四、API的使用例子
1. pthread_creat、pthread_exit、pthread_join、pthread_self函数
1 #include <stdio.h>2 #include <stdlib.h>3 #include <string.h>4 5 #include <pthread.h>6 7 // 1. int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(* start_rtn)(void *), void *restrict arg);8 // 2. pthread_t pthread_self(void);9 // 3. int pthread_join(pthread_t thread, void **rval_ptr);10 // 4. int pthread_exit(void *rval_ptr);11 12 void *func1(void *arg)13 {14 static int data = 10;15 16 printf("t1: %ld\n", pthread_self()); //打印线程ID17 printf("t1: arg = %d\n", *((int *)arg)); //打印传递的参数18 19 pthread_exit((void *)(&data)); //线程退出并返回数据20 }21 22 int main()23 {24 int ret = 0;25 int param = 100;26 int *pret = NULL;27 28 pthread_t t1; //线程ID29 30 ret = pthread_create(&t1, NULL, func1, (void *)¶m); //创建线程31 32 if(ret == 0){33 printf("main: %ld, pthread create success\n", pthread_self());34 }35 36 pthread_join(t1, (void **)(&pret)); //等待线程退出37 38 printf("main: pret = %d\n", *pret); //打印线程退出时反馈的的数据39 40 return 0;41 }
2. pthread_mutex_init、pthread_mutex_destory、pthread_mutex_lock、pthread_mutex_unlock函数
1 #include <stdio.h>2 #include <stdlib.h>3 #include <string.h>4 5 #include <pthread.h>6 7 // 1. int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restric t attr);8 // 2. int pthread_mutex_destroy(pthread_mutex_t *mutex);9 // 3. int pthread_mutex_lock(pthread_mutex_t *mutex);10 // 4. int pthread_mutex_unlock(pthread_mutex_t *mutex);11 12 int g_data = 0;13 14 pthread_mutex_t mutex; //锁15 16 void *func1(void *arg)17 {18 printf("t1: %ld\n", pthread_self()); //打印线程ID19 printf("t1: arg = %d\n", *((int *)arg)); //打印传递过来的数据20 21 pthread_mutex_lock(&mutex); //加锁22 23 while(1){24 printf("t1: %d\n", g_data++);25 26 if(g_data == 3){27 printf("t1 quit===========================\n");28 pthread_mutex_unlock(&mutex); //当g_data = 3时解锁29 pthread_exit(NULL); //线程退出30 }31 32 sleep(1);33 }34 35 }36 37 void *func2(void *arg)38 {39 printf("t2: %ld\n", pthread_self());40 printf("t2: arg = %d\n", *((int *)arg));41 42 while(1){43 pthread_mutex_lock(&mutex); //加锁44 45 printf("t2: %d\n", g_data++);46 47 pthread_mutex_unlock(&mutex); //解锁48 49 sleep(1);50 }51 }52 53 int main()54 {55 int ret = 0;56 int param = 100;57 58 pthread_t t1;59 pthread_t t2;60 61 pthread_mutex_init(&mutex, NULL); //创建互斥锁62 63 ret = pthread_create(&t1, NULL, func1, (void *)¶m); //创建线程164 65 if(ret == 0){66 //printf("main: t1:%ld, pthread create success\n", pthread_self());67 }68 69 ret = pthread_create(&t2, NULL, func2, (void *)¶m); //创建线程270 71 if(ret == 0){72 //printf("main: t2:%ld, pthread create success\n", pthread_self());73 }74 75 pthread_join(t1, NULL); //等待线程1退出76 pthread_join(t2, NULL); //等待线程2退出77 78 pthread_mutex_destroy(&mutex); //销毁互斥锁79 80 return 0;81 }
3. pthread_cond_init、pthread_cond_destory、pthread_cond_wait、pthread_cond_signal函数
1. 动态初始化
1 #include <stdio.h>2 #include <stdlib.h>3 #include <string.h>4 5 #include <pthread.h>6 7 // 1. int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(* start_rtn)(void *), void *restrict arg);8 // 2. pthread_t pthread_self(void);9 // 3. int pthread_join(pthread_t thread, void **rval_ptr);10 // 4. int pthread_exit(void *rval_ptr);11 // 5. int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mut exattr_t *res trict attr);12 // 6. int pthread_mutex_destroy(pthread_mutex_t *mutex);13 // 7. int pthread_mutex_lock(pthread_mutex_t *mutex);14 // 8. int pthread_mutex_unlock(pthread_mutex_t *mutex);15 // 9. int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict at tr);16 // 10. int pthread_cond_destroy(pthread_cond_t *cond);17 // 11. int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);18 // 12. int pthread_cond_signal(pthread_cond_t *cond);19 20 int g_data = 0;21 22 pthread_cond_t cond; //条件变量23 pthread_mutex_t mutex; //锁24 25 void *func1(void *arg)26 {27 printf("t1: %ld\n", pthread_self()); //打印线程ID28 printf("t1: arg = %d\n", *((int *)arg)); //打印传递过来的数据29 30 while(1){31 pthread_cond_wait(&cond, &mutex); //等待条件为真32 33 printf("t1 run=========================\n");34 printf("t1: %d\n", g_data);35 g_data = 0;36 37 sleep(1);38 }39 }40 41 void *func2(void *arg)42 {43 printf("t2: %ld\n", pthread_self());44 printf("t2: arg = %d\n", *((int *)arg));45 46 while(1){47 pthread_mutex_lock(&mutex); //加锁48 49 printf("t2: %d\n", g_data++);50 if(g_data == 3){51 pthread_cond_signal(&cond); //当g_data = 3时,触发条件,唤醒等待该条件的线程52 }53 54 pthread_mutex_unlock(&mutex); //解锁55 56 sleep(1);57 }58 }59 60 int main()61 {62 int ret = 0;63 int param = 100;64 65 pthread_t t1;66 pthread_t t2;67 68 pthread_cond_init(&cond, NULL); //创建条件变量69 pthread_mutex_init(&mutex, NULL); //创建互斥锁70 71 ret = pthread_create(&t1, NULL, func1, (void *)¶m); //创建线程172 73 if(ret == 0){74 //printf("main: t1:%ld, pthread create success\n", pthread_self());75 }76 77 ret = pthread_create(&t2, NULL, func2, (void *)¶m); //创建线程278 79 if(ret == 0){80 //printf("main: t2:%ld, pthread create success\n", pthread_self());81 }82 83 pthread_join(t1, NULL); //等待线程1退出84 pthread_join(t2, NULL); //等待线程2退出85 86 pthread_cond_destroy(&cond); //销毁条件变量87 pthread_mutex_destroy(&mutex); //销毁互斥锁88 89 return 0;90 }
2. 静态初始化
1 #include <stdio.h>2 #include <stdlib.h>3 #include <string.h>4 5 #include <pthread.h>6 7 // 1. int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(* start_rtn)(void *), void *restrict arg);8 // 2. pthread_t pthread_self(void);9 // 3. int pthread_join(pthread_t thread, void **rval_ptr);10 // 4. int pthread_exit(void *rval_ptr);11 // 5. int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mut exattr_t *res trict attr);12 // 6. int pthread_mutex_destroy(pthread_mutex_t *mutex);13 // 7. int pthread_mutex_lock(pthread_mutex_t *mutex);14 // 8. int pthread_mutex_unlock(pthread_mutex_t *mutex);15 // 9. int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict at tr);16 // 10. int pthread_cond_destroy(pthread_cond_t *cond);17 // 11. int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);18 // 12. int pthread_cond_signal(pthread_cond_t *cond);19 20 int g_data = 0;21 22 pthread_cond_t cond = PTHREAD_COND_INITIALIZER; //初始化条件变量23 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //初始化互斥锁24 25 void *func1(void *arg)26 {27 printf("t1: %ld\n", pthread_self()); //打印线程ID28 printf("t1: arg = %d\n", *((int *)arg)); //打印传递过来的值29 30 while(1){31 pthread_cond_wait(&cond, &mutex); //等待条件为真32 33 printf("t1 run=========================\n");34 printf("t1: %d\n", g_data);35 g_data = 0;36 37 sleep(1);38 }39 }40 41 void *func2(void *arg)42 {43 printf("t2: %ld\n", pthread_self()); //打印线程ID44 printf("t2: arg = %d\n", *((int *)arg)); //打印传递过来的值45 46 while(1){47 pthread_mutex_lock(&mutex); //加锁48 49 printf("t2: %d\n", g_data++);50 if(g_data == 3){51 pthread_cond_signal(&cond); //当g_data = 3时,触发条件,唤醒等待该条件的线程52 }53 54 pthread_mutex_unlock(&mutex); //解锁55 56 sleep(1);57 }58 }59 60 int main()61 {62 int ret = 0;63 int param = 100;64 65 pthread_t t1;66 pthread_t t2;67 68 //pthread_cond_init(&cond, NULL);69 //pthread_mutex_init(&mutex, NULL);70 71 ret = pthread_create(&t1, NULL, func1, (void *)¶m); //创建线程172 73 if(ret == 0){74 //printf("main: t1:%ld, pthread create success\n", pthread_self());75 }76 77 ret = pthread_create(&t2, NULL, func2, (void *)¶m); //创建线程278 79 if(ret == 0){80 //printf("main: t2:%ld, pthread create success\n", pthread_self());81 }82 83 pthread_join(t1, NULL); //等待线程1退出84 pthread_join(t2, NULL); //等待线程2退出85 86 pthread_cond_destroy(&cond); //销毁条件变量87 pthread_mutex_destroy(&mutex); //销毁互斥锁88 89 return 0;90 }
相关文章:
Linux系统编程——线程的学习
学习参考博文: Linux多线程编程初探 Linux系统编程学习相关博文 Linux系统编程——文件编程的学习Linux系统编程——进程的学习Linux系统编程——进程间通信的学习Linux系统编程——网络编程的学习 Linux系统编程——线程的学习 一、概述1. 进程与线程的区别2. 使…...

zemaxMIF曲线图
调制传递函数( Modulation Transfer Function,MTF )是用来形容光学系统成像质量的重要指标。 通过对光学系统像空间进行傅里叶变换,可以得到一张分析图表,来描述像面上对比度和空间频率之间的对应关系。 对比度&…...

【苹果】SpringBoot监听Iphone15邮件提醒,Selenium+Python自动化抢购脚本
前言 🍊缘由 Iphone15来了,两年之约你还记得吗? 两年前,与特别的人有一个特别的约定。虽物是人非,但思念仍在。 遂整合之前iphone13及iphone14的相关抢购代码,完成一个SpringBoot监听Iphone15有货邮件提…...

什么是WhatsApp群发,WhatsApp协议,WhatsApp云控
那么WhatsApp群控云控可以做什么呢? 1、获客 自动化引流,强大的可控性,产品快速拓客 2、导流 一键式傻瓜化自动加好友,群发,朋友圈营销 3、群控 一键式拉群好友,建群,进群 …...

RealVNC viewer 窗口指定默认显示
RealVNC Viewer关于显示器(monitor)的参数有两个,一个是monitor,一个是useallmonitor。 monitor就是指定viewer窗体在哪个显示器上显示的,windows下的默认值是空白,改为\\.\DISPLAY2 就可以在打开远程窗口的时候默认在副屏上显…...
图论20(Leetcode1254.统计封闭岛屿的数目)
代码: class Solution {static int[][] dirs {{1,0},{-1,0},{0,1},{0,-1}};public int closedIsland(int[][] grid) {int num 0; for(int i0;i<grid.length;i){for(int j0;j<grid[0].length;j){if(grid[i][j]0){int[] start {i,j};if(getIsland(start,gri…...
Docker 的基本概念和优势,以及在应用程序开发中的实际应用
Docker是一种开源的容器化平台,它可以将应用程序打包成容器,并且可以在不同的环境中运行。Docker的基本概念包括: 镜像(Image):Docker镜像是一个可执行的包,它包含了运行应用程序所需的所有文件…...

数据仓库整理
数仓 olap vs oltp OLTP主要用于支持日常的业务操作,如银行交易、电子商务等,强调数据的准确性、实时性和并发性。OLAP主要用于支持复杂的数据分析,如数据仓库、决策支持等,强调数据的维度、聚合和可视化。 将OLTP数据库的数据…...

《C++API设计》读书笔记(3):模式
本章内容 本章涵盖了一些与CAPI设计相关的设计模式和惯用法。 “设计模式(Design Pattern)”表示软件设计问题的一些通用解决方案。该术语来源于《设计模式:可复用面向对象软件的基础》(Design Patterns: Elements of Reusable Object-Oriented Softwar…...
小程序搜索词优化:小陈运营的秘密武器
大家好,我是小陈,今天要和大家分享一下小程序搜索词优化的经验和技巧。在数字化时代,小程序已经成为许多企业的重要工具,但要让小程序在竞争激烈的市场中脱颖而出,搜索词优化是不可或缺的一环。在本文中,我…...

SpringSecurity 入门
文章目录 Spring Security概念快速入门案例环境准备Spring配置文件SpringMVC配置文件log4j配置文件web.xmlTomcat插件 整合SpringSecurity 认证操作自定义登录页面关闭CSRF拦截数据库认证加密认证状态记住我授权注解使用标签使用 Spring Security概念 Spring Security是Spring…...
【每日一题Day335】LC1993树上的操作 | dfs
树上的操作【LC1993】 给你一棵 n 个节点的树,编号从 0 到 n - 1 ,以父节点数组 parent 的形式给出,其中 parent[i] 是第 i 个节点的父节点。树的根节点为 0 号节点,所以 parent[0] -1 ,因为它没有父节点。你想要设计…...

FPGA:卷积编码及维特比译码仿真
FPGA:卷积编码及维特比译码仿真 本篇记录一下在FPGA中完成卷积编码和维特比译码的过程,通过代码解释编码的过程和译码的过程,便于理解,同时也方便移植到其他工程中。 1. 准备工作 卷积编译码IP核—convolutionIP核和viterbiIP核…...

MySQL学习笔记4
客户端工具的使用: MySQL: mysql命令行工具,一般用来连接访问mysql的数据。 案例:使用mysql客户端工具连接服务器端(用户名:root;密码:123456). [rootmysql-server ~]#…...
JavaFX:窗体显示状态,模态非模态
程序窗体显示一般有3中模式。非模态和模态,其中模态又分为程序模态和窗体模态。 非模态可以理解为窗体之间没有任何限制,可以用鼠标、键盘等工具在窗体间切换。 程序模态是窗体打开后,该程序的所有窗体都被冻结,无法切换&#x…...

C++17中std::filesystem::path的使用
C17引入了std::filesystem库(文件系统库, filesystem library)。这里整理下std::filesystem::path的使用。 std::filesystem::path,文件系统路径,提供了对文件系统及其组件(例如路径、常规文件和目录)执行操作的工具。此path类主要用法包括&#x…...
命令模式简介
概念: 命令模式是一种行为设计模式,它将请求封装成一个对象,从而允许您将不同的请求参数化、队列化,并且能够在不同的时间点执行。通过引入命令对象(Command)来解耦发送者(Invoker)…...
Boost序列化指针
Boost.Serialization 还能序列化指针和引用。 由于指针存储对象的地址,序列化对象的地址没有什么意义,而是在序列化指针和引用时,对象的引用被自动地序列化。 代码 #include <boost/archive/text_oarchive.hpp> #include <boost/…...

NIO简单介绍
一、什么是NIO 1、Java NIO全称java non-blocking IO, 是指JDK提供的新API。从JDK1.4开始,Java提供了一系列改进的输入/输出的新特性,被统称为NIO(即New IO),是同步非阻塞的 2、NIO有三大核心部分: Channel(通道), Buf…...

linux进程杀不死
项目场景: 虚拟机 问题描述 linux进程杀不死 无反应 原因分析: 进程僵死zombie 解决方案: 进proc或者find命令找到进程所在地址 cat status查看进程杀死子进程...

铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...

(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制
在数字化浪潮席卷全球的今天,数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具,在大规模数据获取中发挥着关键作用。然而,传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时,常出现数据质…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...
Java求职者面试指南:计算机基础与源码原理深度解析
Java求职者面试指南:计算机基础与源码原理深度解析 第一轮提问:基础概念问题 1. 请解释什么是进程和线程的区别? 面试官:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位;而线程是进程中的…...