00000008_C并发编程与多线程

并发编程与多线程

并发编程在 C 语言中的应用非常广泛,特别是在高性能计算、服务器开发和嵌入式系统中。并发编程是一种设计思想,而多线程则是其常见的实现方式。虽然 C11 标准引入了对多线程的支持,但在很多应用中,POSIX 线程(pthread)仍占据主导地位,尤其是在跨平台开发和操作系统底层编程中。POSIX 线程提供了一套标准化的多线程编程接口,涵盖了线程创建、同步机制(如互斥锁、条件变量等)以及线程管理等功能,这使其成为实现高效并发应用的首选工具。

接下来,我们将深入探讨如何使用 POSIX 线程进行多线程编程。

1. POSIX线程(pthread)

1.1 线程的创建、终止与同步

在C语言中,使用pthread库进行多线程编程时,常用的操作包括创建线程、终止线程和等待线程结束。

1.1.1 线程的创建与终止

线程创建是通过pthread_create()函数来实现的,该函数原型如下:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
  • thread:返回创建的线程ID。
  • attr:线程的属性,通常传入NULL表示使用默认属性。
  • start_routine:线程执行的函数。
  • arg:传递给线程函数的参数。

线程的终止使用pthread_exit()或返回的方式,一旦线程执行完start_routine函数的代码,线程会自动退出。

创建线程的简单示例:

#include <pthread.h>
#include <stdio.h>void* print_message(void* msg) {printf("%s\n", (char*)msg);return NULL;
}int main() {pthread_t thread;char *message = "Hello, World!";pthread_create(&thread, NULL, print_message, (void*)message);pthread_join(thread, NULL);  // 等待线程结束return 0;
}
1.1.2 线程的同步

线程同步是为了避免多个线程并发访问共享资源时发生数据竞争,常用的同步机制有互斥锁、条件变量和信号量。

1.2 线程间通信:条件变量与信号量

1.2.1 条件变量

条件变量用于线程之间的同步,允许一个线程在某个条件成立时通知另一个线程继续执行。

pthread_cond_wait()函数用于阻塞线程,直到收到条件变量的通知。其原型如下:

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
  • cond:条件变量。
  • mutex:互斥锁,必须先加锁。

条件变量的使用示例:

#include <pthread.h>
#include <stdio.h>pthread_mutex_t mutex;
pthread_cond_t cond;void* wait_for_signal(void* arg) {pthread_mutex_lock(&mutex);printf("Waiting for condition...\n");pthread_cond_wait(&cond, &mutex);printf("Condition met, thread resuming.\n");pthread_mutex_unlock(&mutex);return NULL;
}void* signal_condition(void* arg) {pthread_mutex_lock(&mutex);printf("Signaling condition...\n");pthread_cond_signal(&cond);  // 通知等待线程pthread_mutex_unlock(&mutex);return NULL;
}int main() {pthread_t thread1, thread2;pthread_mutex_init(&mutex, NULL);pthread_cond_init(&cond, NULL);pthread_create(&thread1, NULL, wait_for_signal, NULL);pthread_create(&thread2, NULL, signal_condition, NULL);pthread_join(thread1, NULL);pthread_join(thread2, NULL);pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);return 0;
}

注意事项:

  1. pthread_cond_wait()必须在互斥锁保护下调用,因为它会释放锁并进入阻塞状态,避免其他线程在未同步的情况下修改共享数据。
  2. 条件变量唤醒时,最好使用pthread_cond_wait的循环,因为可能存在虚假唤醒(即不等条件满足时,线程也会被唤醒)。
1.2.2 信号量

信号量用于控制对资源的访问,尤其是在并发量较高时进行资源的计数管理。POSIX信号量在semaphore.h中定义,使用sem_wait()sem_post()等函数来进行操作。

信号量的简单使用示例:

#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>sem_t sem;void* thread_func(void* arg) {sem_wait(&sem);  // 信号量减1printf("Thread is running.\n");sem_post(&sem);  // 信号量加1return NULL;
}int main() {pthread_t threads[5];sem_init(&sem, 0, 3);  // 信号量初始值为3for (int i = 0; i < 5; ++i) {pthread_create(&threads[i], NULL, thread_func, NULL);}for (int i = 0; i < 5; ++i) {pthread_join(threads[i], NULL);}sem_destroy(&sem);return 0;
}

在这个例子中,信号量用于限制同时运行的线程数量为3。

2. 线程同步与互斥

2.1 互斥锁

互斥锁用于保护共享资源,防止多个线程同时访问时发生数据竞争。

互斥锁的创建、使用和销毁:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;pthread_mutex_lock(&mutex);
// 执行临界区代码
pthread_mutex_unlock(&mutex);
2.2 读写锁

读写锁(pthread_rwlock_t)允许多个读线程并发执行,但写线程访问时是独占的。

pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;// 读操作
pthread_rwlock_rdlock(&rwlock);
// 执行读操作
pthread_rwlock_unlock(&rwlock);// 写操作
pthread_rwlock_wrlock(&rwlock);
// 执行写操作
pthread_rwlock_unlock(&rwlock);
2.3 死锁避免

死锁发生的原因通常是多个线程在不同的顺序上请求锁。避免死锁的常见策略:

  1. 锁顺序一致性:保证多个线程按照相同的顺序请求锁。
  2. 尝试锁(pthread_mutex_trylock():尝试获取锁而不阻塞,避免长时间等待。

2.4 原子操作与内存屏障

原子操作是不会被中断的操作,通常用于处理共享数据。

__sync_fetch_and_add(&counter, 1);  // 原子加操作

内存屏障是用来强制执行一定的操作顺序,防止编译器优化引发并发问题。

__sync_synchronize();  // 强制内存屏障

3. 线程池

3.1 线程池的实现与使用

线程池通过创建固定数量的线程来处理多个任务,避免频繁创建销毁线程的开销。

简单的线程池实现步骤:

  1. 创建固定数量的工作线程。
  2. 使用队列存储任务。
  3. 线程从队列中取任务并执行。

线程池示例代码(简化版):

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>#define NUM_THREADS 4
#define NUM_TASKS 10pthread_mutex_t task_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t task_cond = PTHREAD_COND_INITIALIZER;
int tasks[NUM_TASKS];
int task_count = 0;void* thread_func(void* arg) {while (1) {pthread_mutex_lock(&task_mutex);while (task_count == 0) {pthread_cond_wait(&task_cond, &task_mutex);  // 等待任务}int task = tasks[--task_count];  // 获取任务pthread_mutex_unlock(&task_mutex);printf("Thread %ld processing task %d\n", (long)arg, task);}return NULL;
}void add_task(int task) {pthread_mutex_lock(&task_mutex);tasks[task_count++] = task;pthread_cond_signal(&task_cond);  // 通知线程处理任务pthread_mutex_unlock(&task_mutex);
}int main() {pthread_t threads[NUM_THREADS];for (long i = 0; i < NUM_THREADS; i++) {pthread_create(&threads[i], NULL, thread_func, (void*)i);}for (int i = 0; i < NUM_TASKS; i++) {add_task(i);  // 向线程池中添加任务}for (long i = 0; i < NUM_THREADS; i++) {pthread_join(threads[i], NULL);}return 0;
}
3.2 提高多线程效率的技巧
  • 减少锁争用:通过将共享数据划分成多个区域,减少锁竞争。例如,使用分区数据结构,每个线程处理不同部分数据。
  • 合理分配任务:确保每线程闲置的具体方法。

个线程的任务量大致均衡,避免出现线程饥饿或负载不均的情况。可以通过任务分配算法来优化任务的分配,确保工作线程始终有任务可做。

  • 避免过度同步:同步操作(如加锁、条件变量)通常会带来性能开销,因此需要尽量减少锁的粒度,避免锁的持有时间过长。对于读多写少的情况,可以考虑使用读写锁,允许多个读线程并发。

  • 线程优先级管理:合理调整线程优先级,避免低优先级线程长期被高优先级线程抢占而无法执行。这通常需要结合操作系统提供的线程调度接口来进行管理。

  • 使用线程池时要确保池的大小合适:线程池的大小应根据任务的特性、CPU 核心数以及系统的并发能力来动态调整。线程池过大会造成线程上下文切换开销过大,过小则可能导致处理任务的能力不足。

4. 高级线程池设计与优化

线程池设计的核心目标是高效地调度和管理线程,以实现高并发的任务处理。一个高效的线程池通常具有以下几个特点:

  1. 任务队列的设计:任务队列需要设计成一个线程安全的数据结构,通常会使用环形缓冲区、FIFO队列等。常见的线程池任务队列有阻塞队列和非阻塞队列两种。阻塞队列会使得线程在队列为空时被挂起,直到有新任务加入。

  2. 动态扩展线程池大小:通过动态调整线程池的大小来应对任务的波动。例如,当任务积压时,线程池可以自动创建新的线程进行处理;当任务量较少时,线程池可以销毁空闲线程以节省资源。

  3. 线程复用:线程池中的线程通常是复用的,即线程完成一个任务后不会退出,而是回到线程池中等待下一个任务。这避免了频繁的线程创建和销毁开销。

  4. 任务调度策略:为了提高线程池的利用率,任务调度策略应尽量减少空闲等待时间。例如,可以使用优先级队列对任务进行排序,优先执行重要或紧急任务,或者使用延时任务队列处理定时任务。

5. 代码优化和调试技巧

在多线程编程中,调试和优化非常关键。以下是一些有用的调试技巧:

  • 使用日志记录:通过适当的日志记录,帮助跟踪多线程程序的执行流程,特别是线程创建、销毁、锁的获取与释放等关键点。日志输出应该尽量减少对程序性能的影响,可以使用条件编译来在调试模式下打开日志,发布模式下关闭日志。

  • 使用线程调度工具:许多操作系统提供了线程调度和性能分析工具,例如 Linux 的 tophtopperfgdb 等,可以帮助监控多线程程序的资源消耗和执行情况。

  • 避免死锁:死锁是多线程编程中的常见问题,调试死锁时,通常可以利用 gdb 等调试工具查看线程的栈信息。使用日志记录线程的锁获取顺序也是防止死锁的有效方法。

  • 检测竞争条件:竞争条件通常表现为数据的不一致或不可预知的行为。可以使用诸如 valgrindHelgrindThreadSanitizer 等工具进行线程安全性检查,帮助发现并发访问共享资源时的问题。

6. C语言中的多线程编程总结

多线程编程能够有效地提升程序的并发处理能力,尤其在现代的多核处理器上,合理地设计并发系统能显著提高程序的性能。在C语言中,通过 POSIX 线程(pthread)库,我们可以较为简便地实现多线程的创建、同步与通信,同时也能进行线程池的管理。

关键概念总结:

  • 使用pthread_create()创建线程,pthread_join()等待线程结束。
  • 使用互斥锁(pthread_mutex_t)保证对共享资源的访问互斥。
  • 使用条件变量(pthread_cond_t)实现线程间的通信。
  • 使用信号量(sem_t)控制对资源的访问,避免过多线程同时访问。
  • 线程池可以有效地管理线程和任务的关系,减少线程创建销毁的开销,提升并发性能。
  • 通过合理的线程设计、调度策略和同步机制,可以避免死锁、资源争用等问题,提高程序的稳定性与性能。

7. 示例:更复杂的线程池实现

以下是一个更完整的线程池示例,包含了任务队列、线程池管理、任务调度等功能。这个线程池支持动态扩展线程数量,并能够处理大量任务。

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>// 任务结构体
typedef struct task {void (*function)(void* arg);  // 任务执行函数void* arg;                    // 任务参数struct task* next;            // 下一个任务
} task_t;// 线程池结构体
typedef struct thread_pool {pthread_mutex_t mutex;      // 保护任务队列的互斥锁pthread_cond_t cond;        // 任务队列的条件变量task_t* task_queue_head;    // 任务队列头pthread_t* threads;         // 线程数组int num_threads;            // 线程池中线程的数量int stop;                   // 线程池停止标志
} thread_pool_t;// 线程池创建
thread_pool_t* thread_pool_create(int num_threads) {thread_pool_t* pool = malloc(sizeof(thread_pool_t));pool->task_queue_head = NULL;pool->num_threads = num_threads;pool->stop = 0;pool->threads = malloc(sizeof(pthread_t) * num_threads);pthread_mutex_init(&pool->mutex, NULL);pthread_cond_init(&pool->cond, NULL);for (int i = 0; i < num_threads; i++) {pthread_create(&pool->threads[i], NULL, thread_pool_worker, pool);}return pool;
}// 工作线程函数
void* thread_pool_worker(void* arg) {thread_pool_t* pool = (thread_pool_t*)arg;while (1) {pthread_mutex_lock(&pool->mutex);while (pool->task_queue_head == NULL && !pool->stop) {pthread_cond_wait(&pool->cond, &pool->mutex);}if (pool->stop) {pthread_mutex_unlock(&pool->mutex);break;}task_t* task = pool->task_queue_head;pool->task_queue_head = task->next;pthread_mutex_unlock(&pool->mutex);task->function(task->arg);free(task);}return NULL;
}// 向线程池添加任务
void thread_pool_add_task(thread_pool_t* pool, void (*function)(void* arg), void* arg) {task_t* new_task = malloc(sizeof(task_t));new_task->function = function;new_task->arg = arg;new_task->next = NULL;pthread_mutex_lock(&pool->mutex);if (pool->task_queue_head == NULL) {pool->task_queue_head = new_task;} else {task_t* current = pool->task_queue_head;while (current->next) {current = current->next;}current->next = new_task;}pthread_cond_signal(&pool->cond);pthread_mutex_unlock(&pool->mutex);
}// 线程池销毁
void thread_pool_destroy(thread_pool_t* pool) {pthread_mutex_lock(&pool->mutex);pool->stop = 1;pthread_cond_broadcast(&pool->cond);  // 唤醒所有线程pthread_mutex_unlock(&pool->mutex);for (int i = 0; i < pool->num_threads; i++) {pthread_join(pool->threads[i], NULL);}free(pool->threads);free(pool);
}// 示例任务
void sample_task(void* arg) {printf("Executing task with arg: %d\n", *(int*)arg);sleep(1);
}int main() {thread_pool_t* pool = thread_pool_create(4);for (int i = 0; i < 10; i++) {int* task_arg = malloc(sizeof(int));*task_arg = i;thread_pool_add_task(pool, sample_task, task_arg);}sleep(5);  // 等待任务完成thread_pool_destroy(pool);return 0;
}

9. 高级优化技巧

9.1. 无锁编程(Lock-Free Programming)

无锁编程使用原子操作来避免传统的锁,减少锁争用,提升系统性能。在多线程环境下,原子操作可以保证线程间的安全操作,避免数据竞态。

  • CAS(Compare-And-Swap):这是一个常见的原子操作,它用于检查某个值是否等于预期值,如果是,则将其更新为新值。
#include <stdio.h>
#include <stdatomic.h>atomic_int value = ATOMIC_VAR_INIT(0);void *increment(void *arg) {int expected = atomic_load(&value);int desired = expected + 1;atomic_compare_exchange_strong(&value, &expected, desired);return NULL;
}int main() {pthread_t threads[10];for (int i = 0; i < 10; i++) {pthread_create(&threads[i], NULL, increment, NULL);}for (int i = 0; i < 10; i++) {pthread_join(threads[i], NULL);}printf("Final value: %d\n", atomic_load(&value));return 0;
}

在这个例子中,多个线程同时增加value的值,通过atomic_compare_exchange_strong实现了无锁的更新操作。

  • 环形缓冲区:无锁队列的实现,利用原子操作控制读写指针位置。
#include <stdio.h>
#include <stdatomic.h>
#include <pthread.h>#define BUFFER_SIZE 10typedef struct {int buffer[BUFFER_SIZE];atomic_int read_pos;atomic_int write_pos;
} ring_buffer;ring_buffer rb = {{0}, ATOMIC_VAR_INIT(0), ATOMIC_VAR_INIT(0)};void *producer(void *arg) {int value = 0;while (1) {int write_pos = atomic_load(&rb.write_pos);int next_write_pos = (write_pos + 1) % BUFFER_SIZE;if (next_write_pos != atomic_load(&rb.read_pos)) {rb.buffer[write_pos] = value++;atomic_store(&rb.write_pos, next_write_pos);}}return NULL;
}void *consumer(void *arg) {while (1) {int read_pos = atomic_load(&rb.read_pos);if (read_pos != atomic_load(&rb.write_pos)) {printf("Consumed: %d\n", rb.buffer[read_pos]);atomic_store(&rb.read_pos, (read_pos + 1) % BUFFER_SIZE);}}return NULL;
}int main() {pthread_t prod, cons;pthread_create(&prod, NULL, producer, NULL);pthread_create(&cons, NULL, consumer, NULL);pthread_join(prod, NULL);pthread_join(cons, NULL);return 0;
}
9.2. 批量任务处理

通过将多个小任务合并为一个批量任务,能够减少线程切换和锁操作的开销。批量任务处理适合在高负载和大量任务的情况下使用。

#include <stdio.h>
#include <pthread.h>#define TASK_COUNT 1000
#define BATCH_SIZE 100void process_batch(int start, int end) {for (int i = start; i < end; i++) {// 执行任务printf("Processing task %d\n", i);}
}void *task_handler(void *arg) {int batch_start = (int)arg;int batch_end = batch_start + BATCH_SIZE;process_batch(batch_start, batch_end);return NULL;
}int main() {pthread_t threads[TASK_COUNT / BATCH_SIZE];for (int i = 0; i < TASK_COUNT / BATCH_SIZE; i++) {pthread_create(&threads[i], NULL, task_handler, (void *)(i * BATCH_SIZE));}for (int i = 0; i < TASK_COUNT / BATCH_SIZE; i++) {pthread_join(threads[i], NULL);}return 0;
}
9.3. 线程本地存储(TLS)

线程本地存储(TLS)用于在多线程环境下给每个线程分配独立的内存空间,避免线程之间的数据竞争。

#include <pthread.h>
#include <stdio.h>pthread_key_t key;void destructor(void *value) {free(value);
}void *thread_func(void *arg) {int *local_data = malloc(sizeof(int));*local_data = (intptr_t)arg;pthread_setspecific(key, local_data);printf("Thread %d, local data: %d\n", (intptr_t)arg, *local_data);return NULL;
}int main() {pthread_key_create(&key, destructor);pthread_t threads[5];for (int i = 0; i < 5; i++) {pthread_create(&threads[i], NULL, thread_func, (void *)(intptr_t)i);}for (int i = 0; i < 5; i++) {pthread_join(threads[i], NULL);}pthread_key_delete(key);return 0;
}
9.4. 减少线程数量

合理的线程池大小可以避免过多的线程引发的上下文切换和调度开销。

#include <pthread.h>
#include <stdio.h>#define THREAD_POOL_SIZE 4void *task(void *arg) {printf("Thread %d processing task\n", (intptr_t)arg);return NULL;
}int main() {pthread_t threads[THREAD_POOL_SIZE];for (int i = 0; i < THREAD_POOL_SIZE; i++) {pthread_create(&threads[i], NULL, task, (void *)(intptr_t)i);}for (int i = 0; i < THREAD_POOL_SIZE; i++) {pthread_join(threads[i], NULL);}return 0;
}

10. 典型应用场景

10.1. Web 服务器

Web 服务器通常需要同时处理多个客户端请求。使用线程池可以高效地管理并发请求,减少线程的创建和销毁开销。

// 使用线程池处理HTTP请求的伪代码
void handle_request(int client_socket) {// 处理客户端请求
}void *worker_thread(void *arg) {int client_socket = (intptr_t)arg;handle_request(client_socket);return NULL;
}int main() {pthread_t thread_pool[THREAD_POOL_SIZE];int client_socket = accept_connection();for (int i = 0; i < THREAD_POOL_SIZE; i++) {pthread_create(&thread_pool[i], NULL, worker_thread, (void *)(intptr_t)client_socket);}for (int i = 0; i < THREAD_POOL_SIZE; i++) {pthread_join(thread_pool[i], NULL);}return 0;
}
10.2. 分布式计算

多线程在分布式计算中也有广泛应用,尤其在任务调度和结果合并上,可以显著提高系统性能。

void *compute_task(void *arg) {// 执行任务计算return NULL;
}int main() {pthread_t threads[5];for (int i = 0; i < 5; i++) {pthread_create(&threads[i], NULL, compute_task, NULL);}for (int i = 0; i < 5; i++) {pthread_join(threads[i], NULL);}return 0;
}
10.3. 数据流处理

多线程在数据流处理场景中也有应用,多个线程可以并行处理数据流中的不同任务,提升数据处理的吞吐量。

// 生产者-消费者模型示例
void *producer(void *arg) {// 生成数据并放入缓冲区return NULL;
}void *consumer(void *arg) {// 从缓冲区获取并处理数据return NULL;
}int main() {pthread_t prod, cons;pthread_create(&prod, NULL, producer, NULL);pthread_create(&cons, NULL, consumer, NULL);pthread_join(prod, NULL);pthread_join(cons, NULL);return 0;
}

11. 线程池与多进程

在计算密集型任务中,使用多进程比多线程可能更有效,因为进程之间不共享内存,不会有线程间的竞争和锁开销。

#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>int main() {pid_t pid = fork();if (pid == 0) {// 子进程代码printf("Child process\n");} else {// 父进程代码printf("Parent process\n");}return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

高等数学学习笔记 ☞ 一元函数微分的基础知识

1. 微分的定义 &#xff08;1&#xff09;定义&#xff1a;设函数在点的某领域内有定义&#xff0c;取附近的点&#xff0c;对应的函数值分别为和&#xff0c; 令&#xff0c;若可以表示成&#xff0c;则称函数在点是可微的。 【 若函数在点是可微的&#xff0c;则可以表达为】…

openai swarm agent框架源码详解及应用案例实战

文章目录 简介数据类型Agent类Response类Result类Swarm类run_demo_loop交互式会话 基础应用agent-handsofffunction-callingcontext_variablestriage_agent 高阶应用通用客服机器人(support bot)构建航班服务agent 参考资料 openai 在24年10月份开源了一个教育性质的多agents协…

【测试】——Cucumber入门

&#x1f4d6; 前言&#xff1a;Cucumber框架是行为驱动&#xff08;BDD&#xff09;框架的一种&#xff0c;通过自然语言站在功能使用者视角&#xff0c;描述编写测试用例。简单来说就是通过feature文件编写脚本&#xff0c;脚本对应java写的方法&#xff0c;会有一个启动器配…

无网络时自动切换备用网络环境

目录 背景目标为什么需要做自动网络切换网络切换手段 网络环境实现思路和代码部署脚本开机自动执行附录连接两个网络时的路由问题 背景 目标 学校实验室有两个网络环境&#xff0c;我电脑使用网线连接稳定但低速的网络A&#xff0c;使用WiFi连接高速但不稳定的网络B。因此&am…

【微服务】1、引入;注册中心;OpenFeign

微服务技术学习引入 - 微服务自2016年起搜索指数持续增长&#xff0c;已成为企业开发大型项目的必备技术&#xff0c;中高级java工程师招聘多要求熟悉微服务相关技术。微服务架构介绍 概念&#xff1a;微服务是一种软件架构风格&#xff0c;以专注于单一职责的多个响应项目为基…

OpenAI 故障复盘 - 阿里云容器服务与可观测产品如何保障大规模 K8s 集群稳定性

本文作者&#xff1a; 容器服务团队&#xff1a;刘佳旭、冯诗淳 可观测团队&#xff1a;竺夏栋、麻嘉豪、隋吉智 一、前言 Kubernetes(K8s)架构已经是当今 IT 架构的主流与事实标准&#xff08;CNCF Survey[1]&#xff09;。随着承接的业务规模越来越大&#xff0c;用户也在使…

docker+ffmpeg+nginx+rtmp 拉取摄像机视频

1、构造程序容器镜像 app.py import subprocess import json import time import multiprocessing import socketdef check_rtmp_server(host, port, timeout5):try:with socket.create_connection((host, port), timeout):print(f"RTMP server at {host}:{port} is avai…

网络安全-web渗透环境搭建-BWAPP(基础篇)

01--所需系统环境&#xff1a; 虚拟主机系统部署&#xff08;vmware&#xff0c;虚拟主机创建、虚拟主机网络配置&#xff08;桥接&#xff0c;便于网络中多个主机都能访问虚拟主机&#xff09;、虚拟软件功能&#xff0c;快照、克隆、镜像文件加载&#xff0c;ova文件制作&am…

SQL Server中可以通过扩展事件来自动抓取阻塞

在SQL Server中可以通过扩展事件来自动抓取阻塞&#xff0c;以下是详细流程&#xff1a; 开启阻塞跟踪配置&#xff1a; • 执行以下SQL语句来启用相关配置&#xff1a; EXEC sp_configureshow advanced options, 1; RECONFIGURE; EXEC sp_configure blocked process thresh…

SpringBoot环境和Maven配置

SpringBoot环境和Maven配置 1. 环境准备2. Maven2.1 什么是Maven2.2 为什么要学 Maven2.3 创建一个 Maven项目2.4 Maven核心功能2.4.1 项目构建2.4.2 依赖管理2.4.3 Maven Help插件 2.5 Maven 仓库2.5.1本地仓库2.5.2 中央仓库2.5.3 私有服务器, 也称为私服 2.6 Maven设置国内源…

C语言初阶习题【25】strcpy的模拟实现

1. 首先先调用下库函数&#xff0c;看它实现了什么 2. 我们自己实现一个strcpy函数 3. 改进1 把*destnation和source 写上去&#xff0c;使用后置 4. 改进2 这里直接把赋值操作放到了while的判断条件里面&#xff0c;然后while循环语句什么都不做&#xff0c;放了一个空语句…

网络基础1 http1.0 1.1 http/2的演进史

http1.0 1.1 http/2的演进史&#x1f60e; &#xff08;连接复用 队头阻塞 服务器推送 2进制分帧&#xff09; 概述 我们主要关注的是应用层 传输层 http协议发展历史 http的报文结构&#xff1a;起始行 Header Body http的典型特征 http存在的典型问题 Keep Alive机制 chun…

C# XPTable 带图片的增删改查(XPTable控件使用说明十三)

今天完成了一个DEMO, XPtable直接增删改查&#xff0c;带富文本图片&#xff0c;这就是XPtable的优势。需要提示的是关于图片编辑后的保存&#xff1a;使用焦点&#xff0c;过滤掉逐条选择显示图片变化冗余保存数据库。 全部代码&#xff1a; using System.Security.Policy; u…

在 Vue 3 集成 e签宝电子合同签署功能

实现 Vue 3 e签宝电子合同签署功能&#xff0c;需要使用 e签宝提供的实际 SDK 或 API。 e签宝通常提供针对不同平台&#xff08;如 Web、Android、iOS&#xff09;的 SDK&#xff0c;而 Web 端一般通过 WebView 或直接使用嵌入式 iframe 来加载合同签署页面。 下面举个 &…

Perturbed-Attention Guidance(PAG) 笔记

Self-Rectifying Diffusion Sampling with Perturbed-Attention Guidance Github 摘要 近期研究表明&#xff0c;扩散模型能够生成高质量样本&#xff0c;但其质量在很大程度上依赖于采样引导技术&#xff0c;如分类器引导&#xff08;CG&#xff09;和无分类器引导&#xff…

(概率论)无偏估计

参考文章&#xff1a;(15 封私信 / 51 条消息) 什么是无偏估计&#xff1f; - 知乎 (zhihu.com) 首先&#xff0c;第一个回答中&#xff0c;马同学图解数学讲解得很形象&#xff0c; 我的概括是&#xff1a;“注意&#xff0c;有一个总体的均值u。然后&#xff0c;如果抽样n个&…

USB 驱动开发 --- Gadget 设备连接 Windows 免驱

环境信息 测试使用 DuoS(Arm CA53&#xff0c; Linux 5.10) 搭建方案验证环境&#xff0c;使用 USB sniff Wirekshark 抓包分析&#xff0c;合照如下&#xff1a; 注&#xff1a;左侧图中设备&#xff1a;1. 蓝色&#xff0c;USB sniff 非侵入工 USB 抓包工具&#xff1b;2. …

OSPF - 2、3类LSA(Network-LSA、NetWork-Sunmmary-LSA)

前篇博客有对常用LSA的总结 2类LSA&#xff08;Network-LSA&#xff09; DR产生泛洪范围为本区域 作用:  描述MA网络拓扑信息和网络信息&#xff0c;拓扑信息主要描述当前MA网络中伪节点连接着哪几台路由。网络信息描述当前网络的 掩码和DR接口IP地址。 影响邻居建立中说到…

开放词汇检测新晋SOTA:地瓜机器人开源DOSOD实时检测算法

在计算机视觉领域&#xff0c;目标检测是一项关键技术&#xff0c;旨在识别图像或视频中感兴趣物体的位置与类别。传统的闭集检测长期占据主导地位&#xff0c;但近年来&#xff0c;开放词汇检测&#xff08;Open-Vocabulary Object Detection-OVOD 或者 Open-Set Object Detec…

【Ubuntu】 Ubuntu22.04搭建NFS服务

安装NFS服务端 sudo apt install nfs-kernel-server 安装NFS客户端 sudo apt install nfs-common 配置/etc/exports sudo vim /etc/exports 第一个字段&#xff1a;/home/lm/code/nfswork共享的目录 第二个字段&#xff1a;指定哪些用户可以访问 ​ * 表示所有用户都可以访…