Linux下_多线程

news2024/11/23 18:40:30

线程
1. 为什么使用线程?
使用fork创建进程以执行新的任务,该方式的代价很高。多个进程间不会直接共享内存线程是进程的基本执行单元,一个进程的所有任务都在线程中执行,进程要想执行任务,必须得有线程,进程至少要有一条线程,程序启动会默认开启一条线程,这条线程被称为主线程或 UI 线程

2. 什么是线程
线程,是进程内部的一个控制序列。即使不使用线程,进程内部也有一个执行线程。
类比:创建一个进程,类似于“克隆”一个家庭,该“家庭”与原来的家庭完全相同但是新“家庭”和原来的家庭完全独立。 进程包含一个或多个线 程,类似与一个家庭,包含一个或多个家庭成员。 家庭内的各成员同时做各自的事情(父亲工作、母亲持家、小孩上学)而对于家庭外部的人来说,这个家庭同时在做多件事情,家庭内的每个成员,就是一个线程。各个家庭成员有自己的个人资源(线程有自己的局部变量)但是所有家庭成员都能共享这个家庭的资源:房子、汽车、家庭的公共资金。
(同一个进程内的各个线程,能够共享整个进程的全局变量,除了线程的局部变量外,其他资源都共享)

注意:单核处理器上,同一个时刻只能运行一个线程。但是对于用户而言,感觉如同同时执行了多个线程一样
(各线程在单核CPU上切换,在一段时间内,同时执行了多个线程)

3. 线程的优点、缺点
优点: 创建线程比创建进程,开销要小。
缺点: 1)多线程编程,需特别小心,很容易发生错误。
2)多线程调试很困难。
3)把一个任务划分为两部分,用两个线程在单处理器上运行时,不一定更快。
除非能确定这两个部分能同时执行、且运行在多处理器上。

4. 线程的应用场合

  1. 需要让用户感觉在同时做多件事情时,比如,处理文档的进程,一个线程处理用户编辑,一个线程同时统计用户的字数。
  2. 当一个应用程序,需要同时处理输入、计算、输出时,可开3个线程,分别处理输入、计算、输出。让用户感觉不到等待。
  3. 高并发编程。

5. 线程的使用
1)线程的创建: pthread_create
原型:int pthread_create (pthread_t *thread, pthread_attr_t *attr, void *(start_routine)(void), void arg);
参数:thread, 指向新线程的标识符。 通过该指针返回所创建线程的标识符。
attr, 用来设置新线程的属性。一般取默认属性,即该参数取NULL
start_routine, 该线程的处理函数该函数的返回类型和参数类型都是void

arg, 线程处理函数start_routine的参数
功能:创建一个新线程,同时指定该线程的属性、执行函数、执行函数的参数通过参数1返回该线程的标识符。

返回值:成功,返回0
失败,返回错误代码

注意:大部分pthread_开头的函数成功时返回0,失败时返回错误码(而不是-1)

注意:使用fork创建进程后,进程马上就启动,但是是和父进程同时执行fork后的代码。
使用pthread_create创建线程后,新线程马上就启动,即执行对应的线程处理函数。

2)线程的终止: pthread_exit
原型:void pthread_exit (void *retval);
功能:在线程函数内部调用该函数。终止该线程,并通过参数retval返回一个指针。该指针不能指向该线程的局部变量。

3)等待指定线程结束: pthread_join

原型:int  pthread_join  (pthread_t   th, void ** thread_return);
功能:类似与进程中的waitpid等待指定的线程结束,并使参数指向该线程函数的返回值(用pthread_exit返回的值);
参数:th,  指定等待的线程;
     thread_return, 指向该线程函数的返回值,线程函数的返回值类型为void*,故该参数的类型为void**;

4)使用线程程序的编译
(1) 编译时,定义宏_REENTRANT
即: gcc -D_REENTRANT (#define REENTRANT)
功能:告诉编译器,编译时需要可重入功能。即使得,在编译时,编译部分函数的可重入版本。

注意:在单线程程序中,整个程序都是顺序执行的,一个函数在同一时刻只能被一个函数调用,但在多线程中,由于并发性,一个函数可能同时被多个函数调用,此时这个函数就成了临界资源,很容易造成调用函数处理结果的相互影响,如果一个函数在多线程并发的环境中每次被调用产生的结果是不确定的,我们就说这个函数是"不可重入的"/"线程不安全"的。

(2) 编译时,指定线程库
即: gcc -lpthread
功能:使用系统默认的NPTL线程库, 即在默认路径中寻找库文件libpthread.so 默认路径为/usr/lib和/usr/local/lib
当系统默认使用的不是NPTL线程库时(系统较老,2003年以前)
指定:gcc -L/usr/lib/nptl -lpthread
补充: -L 指定库文件所在的目录;
-l 指定库文件的名称(-lpthread ,指库文件名为libpthread.so);

总结:一般使用如下形式即可
gcc -D_REENTRANT -lpthread mythread.c -o mythread

实例 main1.c:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

int my_global;

void* my_thread_handle(void *arg) 
{
	int val;

	val = *((int*)arg);

	printf("new thread begin, arg=%d\n", val);
	my_global += val;

	sleep(3);

	pthread_exit(&my_global);

	//  不再执行
	printf("new thread end\n");
}

int main(void)
{
	pthread_t  mythread;
	int arg;
	int ret;
	void *thread_return;

	arg = 100;
	my_global = 1000;

	printf("my_global=%d\n", my_global);
	printf("ready create thread...\n");
	
	ret = pthread_create(&mythread, 0, my_thread_handle, &arg);
	if (ret != 0) {
		printf("create thread failed!\n");
		exit(1);
	}

	printf("wait thread finished...\n");
	ret = pthread_join(mythread, &thread_return);
	if (ret != 0) {
		printf("pthread_join failed!\n");
		exit(1);
	}
	printf("wait thread end, return value is %d\n", *((int*)thread_return));
	printf("my_global=%d\n", my_global);

	printf("create thread finished!\n");
}

5)线程的同步
1)线程的互斥 - 指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访 问是无序的。
2)线程的同步 - 指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。

  1. 问题:
    同一个进程内的各个线程,共享该进程内的全局变量如果多个线程同时对某个全局变量进行访问时,就可能导致竞态?

    解决办法: 对临界区使用信号量、或互斥量。

  2. 信号量和互斥量的选择,对于同步和互斥,使用信号量或互斥量都可以实现。
    使用时,选择更符合语义的手段:
    如果要求最多只允许一个线程进入临界区,则使用互斥量;
    如果要求多个线程之间的执行顺序满足某个约束,则使用信号量;

3. 信号量:
1)什么是信号量
此时所指的“信号量”是指用于同一个进程内多个线程之间的信号量。即POSIX信号量,而不是System V信号量(用于进程之间的同步)
用于线程的信号量的原理,与用于进程之间的信号量的原理相同。都有P操作、V操作。
信号量的表示:sem_t 类型

2) 信号量的初始化:sem_init
原型:int sem_init (sem_t *sem, int pshared, unsigned int value);
功能:对信号量进行初始化
参数:sem, 指向被初始化的信号量
pshared, 0:表示该信号量是该进程内使用的“局部信号量”, 不再被其它进程共享。非0:该信号量可被其他进程共享,Linux不支持这种信号量
value, 信号量的初值。>= 0
返回值:成功,返回0
失败, 返回错误码

3) 信号量的P操作:sem_wait
原型:int sem_wait (sem_t *sem);
返回值:成功,返回0
失败, 返回错误码

4) 信号量的V操作:sem_post
原型:int sem_post (sem_t *sem);
返回值:成功,返回0
失败, 返回错误码

5) 信号量的删除: sem_destroy
原型:int sem_destroy (sem_t *sem);
返回值:成功,返回0
失败, 返回错误码

  1. 实例:
    主线程循环输入字符串,把字符串存放到一个全局缓存中。新线程从全局缓存中读取字符串,统计该字符串的长度。
    直到用户输入end;
    实例main2.c:
#include <pthread.h>
#include <semaphore.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#define BUFF_SIZE 80

char buff[BUFF_SIZE];
sem_t sem;

static void* str_thread_handle(void *arg) 
{
	while(1) {
		//P(sem)
		if (sem_wait(&sem) != 0) {
			printf("sem_wait failed!\n");
			exit(1);
		}
		
		printf("string is: %slen=%d\n", buff, strlen(buff));
		if (strncmp(buff, "end", 3) == 0) {
			break;
		}
	}
}

int main(void)
{
	int ret;
	pthread_t  str_thread;
	void *thread_return;


	ret = sem_init(&sem, 0, 0);
	if (ret != 0) {
		printf("sem_init failed!\n");
		exit(1);
	}

	ret = pthread_create(&str_thread, 0, str_thread_handle, 0);
	if (ret != 0) {
		printf("pthread_create failed!\n");
		exit(1);
	}

	while (1) {
		fgets(buff, sizeof(buff), stdin);

		//V(sem)
		if (sem_post(&sem) != 0) {
			printf("sem_post failed!\n");
			exit(1);
		}
		
		if (strncmp(buff, "end", 3) == 0) {
			break;
		}
	}

	ret = pthread_join(str_thread, &thread_return);
	if (ret != 0) {
		printf("pthread_join failed!\n");
		exit(1);
	}

	ret = sem_destroy(&sem);
	if (ret != 0) {
		printf("sem_destroy failed!\n");
		exit(1);
	}

	return 0;
}
  1. 练习
    创建2个线程(共有主线程、线程1、线程2共3个线程)
    主线程阻塞式等待用户输入字符串
    主线程每接收到一个字符串之后, 线程1就马上对该字符串进行处理。
    线程1的处理逻辑为:统计该字符串的个数,并记录当时的时间。
    线程1把该字符串处理完后,线程2马上就把处理结果写入文件result.txt
    直到用户输入exit.
    multi_pthread.c:
待实现...

4. 互斥量
1)什么是互斥量效果上等同于初值为1的信号量;
互斥量的使用:类型为 pthread_mutex_t

2)互斥量的初始化:pthread_mutex_init
原型:int pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t *attr);
参数:mutex, 指向被初始化的互斥量
attr, 指向互斥量的属性一般取默认属性(当一个线程已获取互斥量后,该线程再次获取该信号量,将导致死锁!)

3) 互斥量的获取: pthread_mutex_lock
原型:int pthread_mutex_lock (pthread_mutex_t *mutex);

4)互斥量的释放:pthread_mutex_unlock
原型:int pthread_mutex_unlock (pthread_mutex_t *mutex);

5)互斥量的删除: pthread_mutex_destroy
原型: int pthread_mutex_destroy (pthread_mutex_t *mutex);

  1. 实例
    最简单的互斥量使用 main3.c:
#include <pthread.h>
#include <semaphore.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#define BUFF_SIZE 80

int global_value = 1000;
pthread_mutex_t  lock;

static void* str_thread_handle(void *arg) 
{
	int i = 0;

	for (i=0; i<10; i++) {
		//pthread_mutex_lock(&lock);

		if (global_value  > 0) {
			// work
			sleep(1);
			printf("soled ticket(%d) to ChildStation(%d)\n",
				global_value, i+1);
		}
		global_value--;
		
		//pthread_mutex_unlock(&lock);
		sleep(1);
	}
}

int main(void)
{
	int ret;
	pthread_t  str_thread;
	void *thread_return;
	int i;

	

	ret = pthread_mutex_init(&lock, 0);
	if (ret != 0) {
		printf("pthread_mutex_init failed!\n");
		exit(1);
	}

	ret = pthread_create(&str_thread, 0, str_thread_handle, 0);
	if (ret != 0) {
		printf("pthread_create failed!\n");
		exit(1);
	}

	for (i=0; i<10; i++) {
		//pthread_mutex_lock(&lock);
		
		if (global_value  > 0) {
			// work
			sleep(1);
			printf("soled ticket(%d) to MainStation(%d)\n",
				global_value, i+1);
		}
		global_value--;
		
		
		//pthread_mutex_unlock(&lock);
		sleep(1);
	}

	ret = pthread_join(str_thread, &thread_return);
	if (ret != 0) {
		printf("pthread_join failed!\n");
		exit(1);
	}

	ret = pthread_mutex_destroy(&lock);
	if (ret != 0) {
		printf("pthread_mutex_destroy failed!\n");
		exit(1);
	}

	return 0;
}

test.c:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>

void *thread_function(void *arg);

#define WORK_SIZE 1024
char work_area[WORK_SIZE];
int time_to_exit = 0;

int main() 
{
    int res;
    pthread_t a_thread;
    void *thread_result;

    res = pthread_create(&a_thread, NULL, thread_function, NULL);
    if (res != 0) {
        perror("Thread creation failed");
        exit(EXIT_FAILURE);
    }
	
    printf("Input some text. Enter 'end' to finish\n");
    while(!time_to_exit) {
        fgets(work_area, WORK_SIZE, stdin);
        while(1) {
            if (work_area[0] != '\0') {
                sleep(1);
            }
            else {
                break;
            }
        }
    }
	
    printf("\nWaiting for thread to finish...\n");
    res = pthread_join(a_thread, &thread_result);
    if (res != 0) {
        perror("Thread join failed");
        exit(EXIT_FAILURE);
    }
    printf("Thread joined\n");
	
    exit(EXIT_SUCCESS);
}

void *thread_function(void *arg) 
{
    sleep(1);
	
    while(strncmp("end", work_area, 3) != 0) {
        printf("You input %d characters\n", strlen(work_area) -1);
        work_area[0] = '\0';
        sleep(1);
        while (work_area[0] == '\0' ) {
            sleep(1);
        }
    }
    time_to_exit = 1;
    work_area[0] = '\0';
    pthread_exit(0);
}

(1) 分析程序的功能
(2) 分析程序存在的隐患
(3) 用互斥量解决该程序的隐患 (不能删除、修改代码,只能添加代码)
修改后为main5.c:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>

void *thread_function(void *arg);
pthread_mutex_t work_mutex; /* protects both work_area and time_to_exit */

#define WORK_SIZE 1024
char work_area[WORK_SIZE];
int time_to_exit = 0;

int main() {
    int res;
    pthread_t a_thread;
    void *thread_result;
    res = pthread_mutex_init(&work_mutex, NULL);
    if (res != 0) {
        perror("Mutex initialization failed");
        exit(EXIT_FAILURE);
    }
    res = pthread_create(&a_thread, NULL, thread_function, NULL);
    if (res != 0) {
        perror("Thread creation failed");
        exit(EXIT_FAILURE);
    }
    pthread_mutex_lock(&work_mutex);
    printf("Input some text. Enter 'end' to finish\n");
    while(!time_to_exit) {
        fgets(work_area, WORK_SIZE, stdin);
        pthread_mutex_unlock(&work_mutex);
        while(1) {
            pthread_mutex_lock(&work_mutex);
            if (work_area[0] != '\0') {
                pthread_mutex_unlock(&work_mutex);
                sleep(1);
            }
            else {
                break;
            }
        }
    }
    pthread_mutex_unlock(&work_mutex);
    printf("\nWaiting for thread to finish...\n");
    res = pthread_join(a_thread, &thread_result);
    if (res != 0) {
        perror("Thread join failed");
        exit(EXIT_FAILURE);
    }
    printf("Thread joined\n");
    pthread_mutex_destroy(&work_mutex);
    exit(EXIT_SUCCESS);
}

void *thread_function(void *arg) {
    sleep(1);
    pthread_mutex_lock(&work_mutex);
    while(strncmp("end", work_area, 3) != 0) {
        printf("You input %d characters\n", strlen(work_area) -1);
        work_area[0] = '\0';
        pthread_mutex_unlock(&work_mutex);
        sleep(1);
        pthread_mutex_lock(&work_mutex);
        while (work_area[0] == '\0' ) {
            pthread_mutex_unlock(&work_mutex);
            sleep(1);
            pthread_mutex_lock(&work_mutex);
        }
    }
    time_to_exit = 1;
    work_area[0] = '\0';
    pthread_mutex_unlock(&work_mutex);
    pthread_exit(0);
}

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

2. 条件变量初始化 pthread_cond_init
原型:int pthread_cond_init (pthread_cond_t *cond, const pthread_condattr_t *attr);
参数:cond, 条件变量指针
attr 条件变量高级属性
man 安装: apt-get install manpages-posix-dev

3. 唤醒一个等待线程 pthread_cond_signal 通知条件变量,唤醒一个等待者
原型: int pthread_cond_signal (pthread_cond_t *cond);
参数:cond, 条件变量指针

4.唤醒所有等待该条件变量的线程pthread_cond_broadcast 广播条件变量
原型: int pthread_cond_broadcast (pthread_cond_t *cond);
参数:cond, 条件变量指针

5.等待条件变量/超时被唤醒pthread_cond_timedwait 等待条件变量cond被唤醒,直到由一个信号或广播,或绝对时间abstime到 * 才唤醒该线程
原型: int pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
参数:cond, 条件变量指针
pthread_mutex_t *mutex 互斥量
const struct timespec *abstime 等待被唤醒的绝对超时时间

6.等待条件变量被唤醒pthread_cond_wait 等待条件变量cond被唤醒(由一个信号或者广播)
原型: int pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex);
参数:cond, 条件变量指针
pthread_mutex_t *mutex 互斥量
常见错误码: [EINVAL] cond或mutex无效,
[EINVAL] 同时等待不同的互斥量
[EINVAL] 主调线程没有占有互斥量

7. 释放/销毁条件变量pthread_cond_destroy 待销毁的条件变量
原型: int pthread_cond_destroy (pthread_cond_t *cond);
参数:cond, 条件变量指针

8.实例main6.c:

#include <stdio.h>
#include <pthread.h>

pthread_mutex_t mutex;
pthread_cond_t cond;

void *thread1(void *arg)
{

	
	while (1) {

		printf("thread1 is running\n");
		pthread_mutex_lock(&mutex);
		pthread_cond_wait(&cond, &mutex);
		printf("thread1 applied the condition\n");
		pthread_mutex_unlock(&mutex);

		sleep(4);
	}

}


void *thread2(void *arg)
{
	while (1) {

		printf("thread2 is running\n");
		pthread_mutex_lock(&mutex);
		pthread_cond_wait(&cond, &mutex);
		printf("thread2 applied the condition\n");
		pthread_mutex_unlock(&mutex);

		sleep(2);

	}

}

int main()
{

	pthread_t thid1, thid2;

	printf("condition variable study!\n");

	pthread_mutex_init(&mutex, NULL);

	pthread_cond_init(&cond, NULL);

	pthread_create(&thid1, NULL, (void *)thread1, NULL);

	pthread_create(&thid2, NULL, (void *)thread2, NULL);

	do {

		pthread_cond_signal(&cond);
       sleep(1);

	} while (1);

	return 0;

}

main7.c pthread_cond_wait 中释放和加锁机制理解:

#include <stdio.h>
#include <pthread.h>

pthread_mutex_t mutex;
pthread_cond_t cond;

void *thread1(void *arg)
{


	while (1) {

		printf("thread1 is running\n");

		pthread_mutex_lock(&mutex);
		printf("thread1 lock..\n");

		pthread_cond_wait(&cond, &mutex);

		printf("thread1 applied the condition\n");

		printf("thread1 unlock..\n");
		pthread_mutex_unlock(&mutex);

		sleep(4);

	}
}


void *thread2(void *arg)
{

	while (1) {

		printf("thread2 is running\n");

		pthread_mutex_lock(&mutex);
		printf("thread2 lock..\n");

		pthread_cond_wait(&cond, &mutex);

		printf("thread2 applied the condition\n");

		printf("thread2 unlock..\n");
		pthread_mutex_unlock(&mutex);

		sleep(2);

	}

}

int main()
{

	pthread_t thid1, thid2;

	printf("condition variable study!\n");

	pthread_mutex_init(&mutex, NULL);

	pthread_cond_init(&cond, NULL);

	pthread_create(&thid1, NULL, (void *)thread1, NULL);

	pthread_create(&thid2, NULL, (void *)thread2, NULL);

	do {
		sleep(10);
		pthread_cond_signal(&cond);

	} while (1);

	return 0;

}

高并发线程池实现: Nginx 线程池剖析
1.传统的并发处理
2.引入线程池
3.线程池实现剖析

并发基本概念:
所谓并发编程是指在在同一台计算机上“同时”处理多个任务。并发是在同一实体上的多个事件。

多线程能解决的问题?
单个进程或线程同时只能处理一个任务,如果有很多请求需要同时处理怎么办?

解决方案: 运用多进程或多线程技术解决

缺 陷:

  1. 创建和销毁线程上花费的时间和消耗的系统资源,甚至可能要比花在处理实际的用户请求的时间和资源要多得多

  2. 活动的线程需要消耗系统资源,如果启动太多,会导致系统由于过度消耗内存或“切换过度”而导致系统资源不足
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/430133.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

11、响应数据

文章目录1、响应JSON1.1、引入开发场景1.2 、jackson.jar ResponseBody1、装填返回值处理器2、返回值初步处理3、获取并使用返回值处理器4、观察如何获取返回值处理器5、返回值处理器接口内部6、返回值处理器支持的类型7、返回值解析器原理1.3、HTTPMessageConverter 原理1、M…

c# 通过webView2模拟登陆小红书网页版,解析无水印视频图片,以及解决X-s,X-t签名验证【2023年4月15日】

一、c# WebView2简介 1.一开始使用WebBrowser&#xff0c;因为WebBrowser控件使用的是ie内核&#xff0c;经过修改注册表切换为Edge内核后&#xff0c; 发现Edge内核版本较低&#xff0c;加载一些视频网站提示“浏览器版本过低“&#xff0c;”视频无法加载“。 2.WebBrowser…

CentOS上PHP源码安装和配置

CentOS上PHP源码安装和配置 此文是在CentOS 7上已经部署了Nginx的基础上进行的 关于CentOS7上安装Nginx&#xff0c;可参考我之前的文章&#xff1a; CentOS上Nginx安装记录 我们现在在这个基础上安装PHP 7。 PHP里面概念挺多的&#xff0c;没想到安装这个PHP需要花那么多时…

SpringBoot 表单提交全局日期格式转换器

参考资料 SpringBoot–LocalDateTime格式转换(前端入参)SpringBoot InitBinder注解绑定请求参数 目录 一. 实现Converter<S, T>接口的方式二. 全局ControllerAdvice InitBinder注解的方式三. RequestMappingHandlerAdapter的方式四. 效果 分析 ⏹当前台的提交数据的Con…

JVM-0418

JVM-字节码篇 虚拟机体系结构 线程共享&#xff1a;堆、方法区 线程私有&#xff1a;虚拟机栈&#xff0c;本地方法栈&#xff0c;程序计数器。其中虚拟机栈中包括局部变量表&#xff0c;和操作数栈。 字节码文件概述 字节码文件是跨平台的吗&#xff1f; 是的 Java虚拟机…

Apache Log4j2(CVE-2021-4101)远程代码执行漏洞复现

文章目录前言影响范围黑盒发现复现准备JNDILADPRMI漏洞复现Dnslog数据外带使用工具进行反弹shell防御与绕过防御绕过参考前言 Apache log4j是Apache的一个开源项目&#xff0c;Java的日志记录工具(同logback)。 log4j2中存在JNDI注入漏洞&#xff0c;当程序记录用户输入的数据…

Qt Quick - FileDialog文件对话框

FileDialog文件对话框使用总结一、概述二、使用三、常用属性四、常用例子1. 单选打开文本文件2. 单选保存文本文件一、概述 FileDialog提供了一个基本的文件选择器的功能&#xff1a;它允许用户选择现有的文件或目录&#xff0c;或者创建新的文件名。 对话框最初是不可见的。…

【性能测试学习】2023最有效的7大性能测试技术(建议收藏)

进入互联网时代&#xff0c;性能测试显得越来越重要&#xff0c;移动应用、web应用和物联网应用都需要进行性能测试和性能调优&#xff0c;而进行性能和负载测试会产生了大量的数据&#xff0c;这些数据难以分析。除了数据分析&#xff0c;我们还会遇到其它一些困难和挑战。 今…

数据结构和算法学习记录——认识二叉搜索树及二叉搜索树的查找操作(递归以及迭代实现-查找操作、查找最大和最小元素)

目录 二叉搜索树 二叉搜索树的一些操作函数 二叉搜索树的查找操作Find 递归实现 迭代实现 查找最大和最小元素 查找最小元素的递归函数 查找最大元素的迭代函数 二叉搜索树 二叉搜索树&#xff08;BST&#xff0c;Binary Search Tree&#xff09;&#xff0c;也称二…

深入了解 Hugging Face 中的生成工具:Generate方法

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️&#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

SSH升级

升级openssh版本一、安装telnet远程管理主机1、检查是否安装telnet2、安装telnet服务二、下载所需的安装包1、下载openssl、openssh、zlib安装包2、安装所需的相关软件3、备份原来的数据4、复制文件到/usr/local/bin/下增加执行权限一、安装telnet远程管理主机 1、检查是否安装…

通达信口袋支点选股公式编写和设置方法答疑

1、口袋支点选股公式成交量条件 在我编写的口袋支点选股公式中&#xff0c;成交量条件为成交量创10日新高。有网友提出&#xff0c;根据书中的定义&#xff0c;口袋支点成交量条件是成交量大于近10日下跌时的最大成交量。 这个问题确实是我没考虑周全&#xff0c;成交量创10日…

【5G NAS】NR 终端侧PDU建立过程以及数据包的过滤和映射

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…

对数据去趋势

对数据去趋势 测量的信号可能显示数据中非固有的整体模式。这些趋势有时会妨碍数据分析&#xff0c;因此必须进行去趋势。 以具有不同趋势的两种心电图 (ECG) 信号为例。ECG 信号对电源干扰等扰动很敏感。加载信号并绘制它们。 load(ecgSignals.mat) t (1:length(ecgl));su…

LaTeX+Overleaf 论文速通教程

一、文本/排版二、章节和段落三、数学公式四、插入图片五、插入表格六、参考文献与交叉引用不使用BibTeX使用BibTeX(推荐)七、交叉引用label和refOverleaf开发界面 latex命令&#xff1a;\命令[可选参数]{必选参数} Latex项目组成&#xff1a; .tex&#xff1a;正文 .bib&…

如何对数据库进行优化

数据库是什么&#xff1f; 简单来说数据库就是将数据按照一定顺序存储到磁盘上的一个软件&#xff0c;我们平时写的sql语句&#xff0c;就是用数据库软件能识别的语言&#xff0c;对数据进行增删改查。其实数据本质上是不存在表里&#xff0c;而是存在磁盘上&#xff0c;所谓的…

掌握亚马逊,沃尔玛,东南亚平台的测评要点,测评事半功倍

测评其实最重要的两个点就是自己的资源和成号率 资源包括;商家资源&#xff0c;中介资源&#xff0c;礼品卡资源&#xff0c;还有买卖账号的渠道&#xff0c;ip资源 成号率这个直接影响的就是你个人投入成本的多&#xff0c;成号率越高&#xff0c;你的成本越低&#xff0c;但…

【Java版oj】day36Rational Arithmetic、Pre-Post

目录 一、Rational Arithmetic &#xff08;1&#xff09;原题再现 &#xff08;2&#xff09;问题分析 &#xff08;3&#xff09;完整代码 二、Pre-Post &#xff08;1&#xff09;原题再现 &#xff08;2&#xff09;问题分析 &#xff08;3&#xff09;完整代码 一、…

十七、小程序报错 真机调试预览失效 Error: Illegal Buffer

报错 电脑端微信开发者工具运行成功而真机调试预览失效 报错 MiniProgramError Illegal Buffer 报错 {errno: 600001, errMsg: “request:fail -200:net::ERR_CERT_COMMON_NAME_INVALID”} 前言&#xff1a;手头有个去年的微信小程序项目 年底甲方不在使用 所以停掉了服务器、…

互联网医院系统构建:探索开源云平台与互联网医院平台源码的融合

互联网医院系统作为一种新型医疗服务模式&#xff0c;将传统的医院门诊转化为在线咨询、远程会诊等形式&#xff0c;帮助患者更加方便地获得专业医疗服务。 在实现互联网医院系统的建设过程中&#xff0c;选择合适的云平台和医院平台源码是至关重要的。 首先&#xff0c;开源…