pthread_mutex_lock函數深度解析

摘要

pthread_mutex_lock是POSIX線程庫中用于實現線程同步的核心函數,它通過對互斥鎖的加鎖操作來確保多個線程對共享資源的安全訪問。本文從互斥鎖的歷史背景和發展脈絡入手,詳細解析了pthread_mutex_lock函數的設計理念、實現機制和使用場景。通過生產者-消費者模型、線程安全數據結構和讀寫鎖三個典型實例,深入探討了該函數在實際應用中的具體實現方式。文章提供了完整的代碼示例、Makefile配置以及Mermaid流程圖和時序圖,詳細說明了編譯運行方法和結果解讀。最后,總結了pthread_mutex_lock的最佳實踐和常見陷阱,為開發者提供全面的技術參考。


解析

1. 背景與核心概念

1.1 歷史背景與發展歷程

多線程編程的概念最早可以追溯到20世紀60年代,但直到90年代初期,隨著對稱多處理(SMP)系統的普及和硬件價格的下降,多線程編程才真正成為主流開發范式。在這個背景下,需要一種標準化的方式來管理線程間的同步問題。

1995年,IEEE制定了POSIX.1c標準(也稱為pthreads),這是線程操作的第一個標準化接口。pthread_mutex_lock作為其中的核心同步原語,為多線程程序提供了一種可靠的互斥機制。

互斥鎖(Mutual Exclusion Lock)的概念源于荷蘭計算機科學家Edsger Dijkstra在1965年提出的信號量(Semaphore)概念。與信號量相比,互斥鎖提供了更簡單的接口和更明確的語義:一次只允許一個線程訪問受保護的資源。

在Linux系統中,pthread庫的實現經歷了從LinuxThreads到NPTL(Native POSIX Threads Library)的演進。NPTL在Linux 2.6內核中引入,提供了更好的性能和可擴展性,使pthread_mutex_lock在各種場景下都能高效工作。

1.2 核心概念解析

互斥鎖(Mutex) 是一種同步原語,用于保護共享資源,防止多個線程同時訪問導致的競態條件。pthread_mutex_lock函數是使用互斥鎖的關鍵接口之一。

關鍵術語說明

術語解釋
臨界區需要互斥訪問的代碼段
競態條件多個線程并發訪問共享資源時的不確定行為
死鎖兩個或多個線程相互等待對方釋放鎖
饑餓線程長時間無法獲取所需資源

互斥鎖的狀態轉移圖

初始化
pthread_mutex_lock()成功
pthread_mutex_unlock()
pthread_mutex_lock()阻塞
Unlocked
Locked
1.3 互斥鎖的屬性與類型

POSIX標準定義了多種互斥鎖類型,每種類型有不同的行為特性:

類型特性適用場景
PTHREAD_MUTEX_NORMAL標準互斥鎖,不檢測死鎖一般用途
PTHREAD_MUTEX_ERRORCHECK檢測錯誤操作(如重復加鎖)調試階段
PTHREAD_MUTEX_RECURSIVE允許同一線程重復加鎖遞歸函數
PTHREAD_MUTEX_DEFAULT系統默認類型,通常是NORMAL兼容性

2. 設計意圖與考量

2.1 核心設計目標

pthread_mutex_lock的設計主要圍繞以下幾個目標:

  1. 原子性保證:確保鎖的獲取和釋放操作是原子的,不會被打斷
  2. 線程阻塞:當鎖不可用時,調用線程能夠高效地進入等待狀態
  3. 公平性:避免線程饑餓,確保等待線程最終能獲得鎖
  4. 性能:在無競爭情況下開銷最小
2.2 實現機制深度剖析

pthread_mutex_lock的實現通常依賴于底層硬件的原子操作指令(如x86的CMPXCHG)和操作系統內核的支持。其內部實現可以概括為以下幾個步驟:

  1. 快速路徑:嘗試通過原子操作直接獲取鎖
  2. 中速路徑:有限次數的自旋等待,避免立即進入內核態
  3. 慢速路徑:進入內核態,將線程加入等待隊列

這種多層次的實現策略能夠在各種競爭情況下都保持良好的性能。

2.3 設計權衡因素

pthread_mutex_lock的設計需要考慮多個權衡因素:

  1. 響應時間 vs 吞吐量:自旋等待可以減少響應時間,但會增加CPU使用率
  2. 公平性 vs 性能:嚴格的公平性保證可能降低整體吞吐量
  3. 通用性 vs 特異性:通用實現適合大多數場景,但特定場景可能需要定制化實現

3. 實例與應用場景

3.1 生產者-消費者模型

生產者-消費者問題是并發編程中的經典問題,pthread_mutex_lock在這里起到關鍵作用。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>#define BUFFER_SIZE 5typedef struct {int buffer[BUFFER_SIZE];int count;int in;int out;pthread_mutex_t mutex;pthread_cond_t not_empty;pthread_cond_t not_full;
} buffer_t;void buffer_init(buffer_t *b) {b->count = 0;b->in = 0;b->out = 0;pthread_mutex_init(&b->mutex, NULL);pthread_cond_init(&b->not_empty, NULL);pthread_cond_init(&b->not_full, NULL);
}void buffer_put(buffer_t *b, int item) {pthread_mutex_lock(&b->mutex);// 等待緩沖區不滿while (b->count == BUFFER_SIZE) {pthread_cond_wait(&b->not_full, &b->mutex);}// 放入項目b->buffer[b->in] = item;b->in = (b->in + 1) % BUFFER_SIZE;b->count++;pthread_cond_signal(&b->not_empty);pthread_mutex_unlock(&b->mutex);
}int buffer_get(buffer_t *b) {int item;pthread_mutex_lock(&b->mutex);// 等待緩沖區不空while (b->count == 0) {pthread_cond_wait(&b->not_empty, &b->mutex);}// 取出項目item = b->buffer[b->out];b->out = (b->out + 1) % BUFFER_SIZE;b->count--;pthread_cond_signal(&b->not_full);pthread_mutex_unlock(&b->mutex);return item;
}void* producer(void *arg) {buffer_t *b = (buffer_t*)arg;for (int i = 0; i < 10; i++) {printf("生產者放入: %d\n", i);buffer_put(b, i);sleep(1);}return NULL;
}void* consumer(void *arg) {buffer_t *b = (buffer_t*)arg;for (int i = 0; i < 10; i++) {int item = buffer_get(b);printf("消費者取出: %d\n", item);sleep(2);}return NULL;
}int main() {buffer_t b;pthread_t prod_thread, cons_thread;buffer_init(&b);pthread_create(&prod_thread, NULL, producer, &b);pthread_create(&cons_thread, NULL, consumer, &b);pthread_join(prod_thread, NULL);pthread_join(cons_thread, NULL);return 0;
}

流程圖

ProducerBufferConsumer初始化緩沖區pthread_mutex_lock()獲取互斥鎖檢查緩沖區是否滿如果滿,等待not_full條件放入物品pthread_cond_signal(not_empty)pthread_mutex_unlock()釋放互斥鎖loop[生產10個物品]pthread_mutex_lock()獲取互斥鎖檢查緩沖區是否空如果空,等待not_empty條件取出物品pthread_cond_signal(not_full)pthread_mutex_unlock()釋放互斥鎖loop[消費10個物品]ProducerBufferConsumer
3.2 線程安全的數據結構

實現一個線程安全的鏈表結構,展示pthread_mutex_lock在數據結構保護中的應用。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>typedef struct node {int data;struct node *next;
} node_t;typedef struct {node_t *head;pthread_mutex_t mutex;
} thread_safe_list_t;void list_init(thread_safe_list_t *list) {list->head = NULL;pthread_mutex_init(&list->mutex, NULL);
}void list_insert(thread_safe_list_t *list, int data) {node_t *new_node = malloc(sizeof(node_t));new_node->data = data;pthread_mutex_lock(&list->mutex);new_node->next = list->head;list->head = new_node;pthread_mutex_unlock(&list->mutex);
}int list_remove(thread_safe_list_t *list, int data) {pthread_mutex_lock(&list->mutex);node_t *current = list->head;node_t *previous = NULL;int found = 0;while (current != NULL) {if (current->data == data) {if (previous == NULL) {list->head = current->next;} else {previous->next = current->next;}free(current);found = 1;break;}previous = current;current = current->next;}pthread_mutex_unlock(&list->mutex);return found;
}void list_print(thread_safe_list_t *list) {pthread_mutex_lock(&list->mutex);node_t *current = list->head;printf("鏈表內容: ");while (current != NULL) {printf("%d ", current->data);current = current->next;}printf("\n");pthread_mutex_unlock(&list->mutex);
}void* thread_func(void *arg) {thread_safe_list_t *list = (thread_safe_list_t*)arg;for (int i = 0; i < 5; i++) {int value = rand() % 100;list_insert(list, value);printf("線程 %ld 插入: %d\n", pthread_self(), value);list_print(list);}return NULL;
}int main() {thread_safe_list_t list;pthread_t threads[3];list_init(&list);for (int i = 0; i < 3; i++) {pthread_create(&threads[i], NULL, thread_func, &list);}for (int i = 0; i < 3; i++) {pthread_join(threads[i], NULL);}list_print(&list);return 0;
}
3.3 讀寫鎖的實現

基于pthread_mutex_lock實現一個簡單的讀寫鎖,展示如何構建更高級的同步原語。

#include <stdio.h>
#include <pthread.h>typedef struct {pthread_mutex_t mutex;pthread_cond_t readers_cond;pthread_cond_t writers_cond;int readers_count;int writers_count;int writers_waiting;
} rwlock_t;void rwlock_init(rwlock_t *rw) {pthread_mutex_init(&rw->mutex, NULL);pthread_cond_init(&rw->readers_cond, NULL);pthread_cond_init(&rw->writers_cond, NULL);rw->readers_count = 0;rw->writers_count = 0;rw->writers_waiting = 0;
}void read_lock(rwlock_t *rw) {pthread_mutex_lock(&rw->mutex);// 等待沒有寫者正在寫或等待寫while (rw->writers_count > 0 || rw->writers_waiting > 0) {pthread_cond_wait(&rw->readers_cond, &rw->mutex);}rw->readers_count++;pthread_mutex_unlock(&rw->mutex);
}void read_unlock(rwlock_t *rw) {pthread_mutex_lock(&rw->mutex);rw->readers_count--;// 如果沒有讀者了,喚醒等待的寫者if (rw->readers_count == 0) {pthread_cond_signal(&rw->writers_cond);}pthread_mutex_unlock(&rw->mutex);
}void write_lock(rwlock_t *rw) {pthread_mutex_lock(&rw->mutex);rw->writers_waiting++;// 等待沒有讀者和寫者while (rw->readers_count > 0 || rw->writers_count > 0) {pthread_cond_wait(&rw->writers_cond, &rw->mutex);}rw->writers_waiting--;rw->writers_count++;pthread_mutex_unlock(&rw->mutex);
}void write_unlock(rwlock_t *rw) {pthread_mutex_lock(&rw->mutex);rw->writers_count--;// 優先喚醒等待的寫者,否則喚醒所有讀者if (rw->writers_waiting > 0) {pthread_cond_signal(&rw->writers_cond);} else {pthread_cond_broadcast(&rw->readers_cond);}pthread_mutex_unlock(&rw->mutex);
}

4. 代碼實現與詳細解析

4.1 pthread_mutex_lock的完整示例

下面是一個完整的示例,展示pthread_mutex_lock在各種場景下的使用:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>#define THREAD_COUNT 5// 全局共享資源
int shared_counter = 0;
pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;// 錯誤檢查互斥鎖
pthread_mutex_t errorcheck_mutex;// 遞歸互斥鎖
pthread_mutex_t recursive_mutex;void init_mutexes() {// 初始化錯誤檢查互斥鎖pthread_mutexattr_t attr;pthread_mutexattr_init(&attr);pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);pthread_mutex_init(&errorcheck_mutex, &attr);// 初始化遞歸互斥鎖pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);pthread_mutex_init(&recursive_mutex, &attr);pthread_mutexattr_destroy(&attr);
}// 簡單的計數器線程函數
void* counter_thread(void* arg) {int id = *(int*)arg;for (int i = 0; i < 10000; i++) {pthread_mutex_lock(&counter_mutex);shared_counter++;pthread_mutex_unlock(&counter_mutex);}printf("線程 %d 完成\n", id);return NULL;
}// 錯誤檢查互斥鎖示例
void* errorcheck_thread(void* arg) {int result;// 第一次加鎖應該成功result = pthread_mutex_lock(&errorcheck_mutex);if (result == 0) {printf("第一次加鎖成功\n");}// 嘗試重復加鎖(應該失敗)result = pthread_mutex_lock(&errorcheck_mutex);if (result == EDEADLK) {printf("檢測到死鎖:重復加鎖\n");}pthread_mutex_unlock(&errorcheck_mutex);return NULL;
}// 遞歸函數使用遞歸互斥鎖
void recursive_function(int depth) {if (depth <= 0) return;pthread_mutex_lock(&recursive_mutex);printf("遞歸深度: %d, 鎖計數增加\n", depth);recursive_function(depth - 1);printf("遞歸深度: %d, 鎖計數減少\n", depth);pthread_mutex_unlock(&recursive_mutex);
}// 遞歸互斥鎖示例
void* recursive_thread(void* arg) {printf("開始遞歸函數演示\n");recursive_function(3);printf("結束遞歸函數演示\n");return NULL;
}int main() {pthread_t threads[THREAD_COUNT];int thread_ids[THREAD_COUNT];init_mutexes();// 測試1: 基本互斥鎖printf("=== 測試1: 基本互斥鎖 ===\n");for (int i = 0; i < THREAD_COUNT; i++) {thread_ids[i] = i;pthread_create(&threads[i], NULL, counter_thread, &thread_ids[i]);}for (int i = 0; i < THREAD_COUNT; i++) {pthread_join(threads[i], NULL);}printf("最終計數器值: %d (期望: %d)\n", shared_counter, THREAD_COUNT * 10000);// 測試2: 錯誤檢查互斥鎖printf("\n=== 測試2: 錯誤檢查互斥鎖 ===\n");pthread_t errorcheck_thread_obj;pthread_create(&errorcheck_thread_obj, NULL, errorcheck_thread, NULL);pthread_join(errorcheck_thread_obj, NULL);// 測試3: 遞歸互斥鎖printf("\n=== 測試3: 遞歸互斥鎖 ===\n");pthread_t recursive_thread_obj;pthread_create(&recursive_thread_obj, NULL, recursive_thread, NULL);pthread_join(recursive_thread_obj, NULL);// 清理資源pthread_mutex_destroy(&errorcheck_mutex);pthread_mutex_destroy(&recursive_mutex);printf("\n所有測試完成\n");return 0;
}
4.2 Makefile配置
# Compiler and flags
CC = gcc
CFLAGS = -Wall -Wextra -pedantic -std=c11 -pthread -D_GNU_SOURCE# Targets
TARGETS = mutex_demo producer_consumer thread_safe_list rwlock_demo# Default target
all: $(TARGETS)# Individual targets
mutex_demo: mutex_demo.c$(CC) $(CFLAGS) -o $@ $<producer_consumer: producer_consumer.c$(CC) $(CFLAGS) -o $@ $<thread_safe_list: thread_safe_list.c$(CC) $(CFLAGS) -o $@ $<rwlock_demo: rwlock_demo.c$(CC) $(CFLAGS) -o $@ $<# Clean up
clean:rm -f $(TARGETS) *.o# Run all demos
run: all@echo "=== 運行互斥鎖演示 ==="./mutex_demo@echo ""@echo "=== 運行生產者-消費者演示 ==="./producer_consumer@echo ""@echo "=== 運行線程安全鏈表演示 ==="./thread_safe_list@echo ""@echo "=== 運行讀寫鎖演示 ==="./rwlock_demo.PHONY: all clean run
4.3 編譯與運行

編譯方法

make

運行方法

make run

預期輸出

=== 運行互斥鎖演示 ===
線程 0 完成
線程 1 完成
線程 2 完成
線程 3 完成
線程 4 完成
最終計數器值: 50000 (期望: 50000)=== 測試2: 錯誤檢查互斥鎖 ===
第一次加鎖成功
檢測到死鎖:重復加鎖=== 測試3: 遞歸互斥鎖 ===
開始遞歸函數演示
遞歸深度: 3, 鎖計數增加
遞歸深度: 2, 鎖計數增加
遞歸深度: 1, 鎖計數增加
遞歸深度: 1, 鎖計數減少
遞歸深度: 2, 鎖計數減少
遞歸深度: 3, 鎖計數減少
結束遞歸函數演示所有測試完成
4.4 pthread_mutex_lock的內部流程
調用 pthread_mutex_lock
鎖是否可用?
獲取鎖
是自旋鎖且自旋次數未滿?
自旋等待
加入等待隊列
線程阻塞
等待喚醒
返回成功

5. 交互性內容解析

5.1 多線程競爭時序分析

當多個線程同時競爭同一個互斥鎖時,pthread_mutex_lock的內部行為可以通過以下時序圖展示:

線程1線程2線程3互斥鎖內核調度器初始狀態: 鎖可用pthread_mutex_lock()鎖狀態: 已鎖定(線程1)返回成功pthread_mutex_lock()鎖狀態: 已鎖定(線程1)線程2加入等待隊列線程2阻塞調度出CPUpthread_mutex_lock()鎖狀態: 已鎖定(線程1)線程3加入等待隊列線程3阻塞調度出CPUpthread_mutex_unlock()鎖狀態: 可用喚醒等待隊列中的線程喚醒線程2調度到CPU獲取鎖成功鎖狀態: 已鎖定(線程2)返回成功pthread_mutex_unlock()鎖狀態: 可用喚醒線程3喚醒線程3調度到CPU獲取鎖成功鎖狀態: 已鎖定(線程3)返回成功線程1線程2線程3互斥鎖內核調度器
5.2 優先級反轉問題

優先級反轉是多線程系統中的經典問題,發生在高優先級線程等待低優先級線程持有的鎖時:

高優先級線程中優先級線程低優先級線程互斥鎖時間點1: 低優先級線程獲取鎖pthread_mutex_lock()成功時間點2: 高優先級線程就緒pthread_mutex_lock()阻塞高優先級線程等待低優先級線程時間點3: 中優先級線程就緒執行任務(無阻塞)低優先級線程被中優先級線程搶占時間點4: 優先級反轉發生高優先級線程間接等待中優先級線程盡管它的優先級更高高優先級線程中優先級線程低優先級線程互斥鎖

解決優先級反轉的方法包括優先級繼承和優先級天花板協議,Linux的pthread_mutex_lock實現了優先級繼承協議。

6. 最佳實踐與常見陷阱

6.1 最佳實踐
  1. 鎖的粒度控制:鎖的粒度應該盡可能小,只保護必要的共享數據,減少鎖的持有時間

  2. 避免嵌套鎖:盡量避免在持有一個鎖的情況下獲取另一個鎖,這容易導致死鎖

  3. 使用RAII模式:在C++中,使用RAII(Resource Acquisition Is Initialization)模式管理鎖的生命周期

    class ScopedLock {
    public:ScopedLock(pthread_mutex_t& mutex) : mutex_(mutex) {pthread_mutex_lock(&mutex_);}~ScopedLock() {pthread_mutex_unlock(&mutex_);}
    private:pthread_mutex_t& mutex_;
    };
    
  4. 錯誤檢查:始終檢查pthread_mutex_lock的返回值,處理可能的錯誤情況

  5. 鎖的順序:如果必須使用多個鎖,確保所有線程以相同的順序獲取鎖

6.2 常見陷阱
  1. 死鎖:多個線程相互等待對方釋放鎖

    • 解決方案:使用鎖層次結構、超時機制或死鎖檢測算法
  2. 活鎖:線程不斷重試某個操作但無法取得進展

    • 解決方案:引入隨機退避機制
  3. 優先級反轉:高優先級線程被低優先級線程阻塞

    • 解決方案:使用優先級繼承協議
  4. 鎖護送(Lock Convoy):多個線程頻繁競爭同一個鎖,導致性能下降

    • 解決方案:減少鎖的粒度或使用無鎖數據結構
  5. 忘記釋放鎖:導致其他線程無法獲取鎖

    • 解決方案:使用RAII模式或靜態分析工具

7. 性能優化技巧

  1. 選擇適當的鎖類型:根據場景選擇最合適的鎖類型(自旋鎖、互斥鎖、讀寫鎖等)

  2. 減少鎖競爭:通過數據分片(sharding)減少對單個鎖的競爭

  3. 使用讀寫鎖:當讀操作遠多于寫操作時,使用讀寫鎖可以提高并發性

  4. 無鎖編程:對于性能關鍵區域,考慮使用無鎖數據結構和算法

  5. 本地化處理:盡可能在線程本地處理數據,減少共享數據的使用

8. 調試與診斷

  1. 使用調試版本:在調試時使用PTHREAD_MUTEX_ERRORCHECK類型,可以檢測常見的錯誤用法

  2. 死鎖檢測工具:使用Helgrind、DRD等工具檢測死鎖和鎖 misuse

  3. 性能分析:使用perf、strace等工具分析鎖競爭情況

  4. 日志記錄:在關鍵部分添加日志記錄,跟蹤鎖的獲取和釋放順序

9. 總結

pthread_mutex_lock是POSIX線程編程中最基礎和重要的同步原語之一。正確理解和使用pthread_mutex_lock對于編寫正確、高效的多線程程序至關重要。通過本文的詳細解析,我們深入探討了:

  1. pthread_mutex_lock的歷史背景和核心概念
  2. 其設計目標和實現機制
  3. 在實際應用中的各種使用場景和示例
  4. 最佳實踐和常見陷阱
  5. 性能優化和調試技巧

掌握pthread_mutex_lock不僅意味著理解一個API函數的用法,更重要的是理解多線程同步的核心思想和原則。在實際開發中,應該根據具體需求選擇合適的同步策略,并始終注意避免常見的并發編程陷阱。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/97809.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/97809.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/97809.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

qt QBoxSet詳解

1、概述QBoxSet 類代表箱形圖中的一個條目。箱形條目是范圍和由五個不同值構成的三個中值的圖形表示。這五個值分別是&#xff1a;下極值、下四分位數、中位數、上四分位數和上極值。QBoxSet 提供了多種方法來設置和獲取這些值&#xff0c;并且可以與 QBoxPlotSeries 和 QChart…

機器學習勢函數(MLPF)入門:用DeePMD-kit加速億級原子模擬

點擊 “AladdinEdu&#xff0c;同學們用得起的【H卡】算力平臺”&#xff0c;注冊即送-H卡級別算力&#xff0c;80G大顯存&#xff0c;按量計費&#xff0c;靈活彈性&#xff0c;頂級配置&#xff0c;學生更享專屬優惠。 引言&#xff1a;從傳統分子模擬到機器學習勢函數的革命…

制作uniapp需要的storyboard全屏ios啟動圖

//鎖定豎屏 plus.screen.lockOrientation("portrait-primary") // #endif首先準備啟動圖兩個dc_launchscreen_portrait_background2x.png(750*1624)dc_launchscreen_portrait_background3x.png(1125*2436)LaunchScreen.storyboard文件內容如下<?xml version"…

OpenCV:答題卡識別

目錄 一、項目原理 二、環境準備 三、核心代碼實現 1. 導入必要庫 2. 定義關鍵函數 坐標點排序函數 透視變換函數 輪廓排序函數 圖像顯示函數 3. 主程序實現 圖像預處理 輪廓檢測與答題卡定位 透視變換矯正 答案識別與評分 四、實現效果 本文將介紹如何使用 Ope…

機器寵物(以四足寵物為主)四肢與關節的系統化設計指南

1. 目標與約束先行 目標&#xff1a;自然步態&#xff08;走/小跑/小跳&#xff09;、安全親和、低噪、跌倒不致損&#xff1b;支持地毯/木地板/瓷磚等家庭地面。約束&#xff1a;體重 1–6 kg&#xff1b;單次續航 ≥ 30–60 min&#xff1b;整機成本與可維護性&#xff1b;室…

spark hive presto doris 對substr函數的差異

Spark、Hive、Presto&#xff08;現更名為 Trino&#xff09;和 Doris&#xff08;原百度 Palo&#xff09;的 substr 函數在功能上都是用于截取字符串的子串&#xff0c;但在起始索引規則和參數含義上存在差異&#xff0c;這是導致結果不同的主要原因。以下是它們的具體區別&a…

開題報告之基于AI Agent智能問答的旅游網站

課題題目&#xff1a; 基于AI Agent智能問答的旅游網站 學生姓名&#xff1a; 學 號&#xff1a; 學 院&#xff1a; 專業年級&#xff1a; 指導教師&#xff1a; 開題報告word版&#xff1a; 開題報告word版 一、課題的研究目的和意義&#xff08;本…

HTB打靶復個小盤

文章目錄jerrySauGoodGamesdevvotexpaper最近打了不少靶場&#xff0c;雖然難度都不算高&#xff0c;但也學到不少東西&#xff0c;中間去打了一周的實網滲透&#xff0c;打完后聯系了一家企業準備面試&#xff0c;感覺面試準備的差不多了&#xff0c;回來繼續打靶&#xff0c;…

云手機的技術架構可分為哪些

一、基礎設施層為其提供計算、存儲和網絡資源&#xff0c;高性能的服務器 CPU 是關鍵&#xff0c;它需具備多核多線程處理能力&#xff0c;以同時支持多個云手機實例的運行&#xff0c;比如英特爾至強系列處理器&#xff0c;能夠有效處理復雜的運算任務&#xff1b;通過虛擬化技…

[創業之路-585]:初創公司的保密安全與信息公開的效率提升

初創公司處于快速發展與資源有限的雙重約束下&#xff0c;平衡保密安全與信息公開效率是生存與發展的關鍵。保密安全可保護核心資產&#xff08;如技術、客戶數據、商業計劃&#xff09;&#xff0c;避免被競爭對手模仿或惡意攻擊&#xff1b;而信息公開的效率則直接影響團隊協…

如何在Docker容器中為Stimulsoft BI Server配置HTTPS安全訪問

在 Stimulsoft BI Server 2025.3.1 版本中&#xff0c;新增了在 Docker 容器中運行 BI Server 的能力。本文將為大家介紹如何在容器環境中為 BI Server 配置 HTTPS 協議的數據傳輸&#xff0c;從而實現安全、加密的訪問。 為什么需要 HTTPS&#xff1f; **HTTPS&#xff08;S…

PPT中將圖片裁剪為愛心等形狀

在WPS演示和PowerPoint中&#xff0c;使用裁剪功能&#xff0c;可以將插入的圖片裁剪為各種形狀&#xff0c;例如心形、五角形、云朵形等等。WPS演示還可以指定裁剪的位置&#xff0c;更加靈活。一、在PowerPoint中裁剪圖片為愛心等形狀將圖片插入到幻燈片后&#xff0c;選中圖…

深入理解Docker網絡:實現容器間的內部訪問

目錄一、利用宿主機 IP 外部端口實現容器互訪1.思路2.示例操作3.訪問測試4.工作原理5.總結二、Docker 容器之間的網絡通信&#xff08;docker0 與自定義橋接網絡&#xff09;1. docker0 簡介2. 通過容器 IP 訪問3. 自定義橋接網絡&#xff08;推薦方式&#xff09;創建自定義網…

ESD靜電保護二極管焊接時需要區分方向和極性嗎?-深圳阿賽姆

ESD靜電保護二極管焊接時需要區分方向和極性嗎&#xff1f;一、ESD二極管極性概述1.1 單向與雙向ESD二極管的基本區別ESD靜電保護二極管根據其內部結構和工作原理可分為兩種主要類型&#xff1a;單向ESD二極管&#xff08;Unidirectional&#xff09;&#xff1a;具有明確的陽極…

Qt QML Switch和SwitchDelegate的區別?

在 Qt QML 中&#xff0c;Switch和 SwitchDelegate主要區別體現在定位、使用場景和功能特性上。以下是具體分析&#xff1a;?1. 核心定位??Switch?&#xff1a;是一個基礎的獨立交互控件?&#xff08;繼承自 ToggleButton&#xff09;&#xff0c;用于直接提供“開/關”&a…

no module name ‘kaolin‘

如果報錯 no module named xxx 一般是沒安裝這個庫&#xff0c;但是各種邪修安裝了kaolin之后&#xff0c;還是報錯&#xff0c;這個報錯的核心信息是&#xff1a; ImportError: .../kaolin/_C.so: undefined symbol: _ZN3c104cuda20CUDACachingAllocator9allocatorE意思是 Ka…

OBS使用教程:OBS歌曲顯示插件如何下載?如何安裝使用?

OBS使用教程&#xff1a;OBS歌曲顯示插件如何下載&#xff1f;如何安裝使用&#xff1f; 第一步&#xff1a;下載OBS歌曲顯示插件&#xff0c;并完成安裝 OBS歌曲顯示插件下載地址①&#xff1a; https://d.obscj.com/obs-Setup_BGM.exe OBS歌曲顯示插件下載地址②&#xf…

基于 Java EE+MySQL+Dart 實現多平臺應用的音樂共享社區

基于多平臺應用的音樂共享社區 1 緒論 1.1 課題依據及意義 隨著互聯網娛樂項目的日益增多&#xff0c;內容也日漸豐富&#xff0c;加之網絡便利性的增強&#xff0c;越來越多的用戶喜歡在網上聽音樂。但是各平臺音樂資源殘次不齊&#xff0c;也包含了許多假無損音樂&#xf…

貪心算法在物聯網能耗優化中的應用

Java中的貪心算法在物聯網能耗優化中的應用 貪心算法是一種在每一步選擇中都采取當前狀態下最優決策的算法策略&#xff0c;它在物聯網(IoT)能耗優化問題中有著廣泛的應用。下面我將全面詳細地講解如何使用Java實現貪心算法來解決物聯網中的能耗優化問題。 一、物聯網能耗優化問…

59.【.NET8 實戰--孢子記賬--從單體到微服務--轉向微服務】--新增功能--MinIO對象存儲服務

在孢子記賬中我們需要存儲用戶的頭像、賬單的圖片等文件&#xff0c;這些文件的存儲我們可以使用MinIO對象存儲服務&#xff0c; MinIO提供了高性能、可擴展的對象存儲解決方案&#xff0c;能夠幫助我們輕松管理這些文件資源。通過MinIO&#xff0c;我們可以將用戶上傳的圖片文…