線程同步的學習與應用

1.多線程并發?

1).多線程并發引例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <pthread.h>int wg=0;
void *fun(void *arg)
{for(int i=0;i<1000;i++){wg++;printf("wg=%d\n",wg);}
}
int main()
{pthread_t id[5];for(int i=0;i<5;i++){pthread_create(&id[i],NULL,fun,NULL);}for(int i=0;i<5;i++){pthread_join(id[i],NULL);}exit(0);
}

?

運行結果出錯!!!

原子操作的概念:一個或多個指令的序列,對外是不可分的;即沒有其他進程可以看到其中間狀態或者中斷此操作;

而wg++不是一個原子操作;

這種情況也不是每一次都發生,也不是一定發生了多少次.這種情況就是最可怕的,給人的感覺是時對時錯;

注意,只有一個處理器的時候這種情況出現的概率是非常小的,同一時刻只有一個線程在運行,不容易出現兩個線程同時去獲取i的值的情況,但是也會發生;?

2).解決多線程并發---線程同步

多線程并發就有可能出現問題的,比如兩個線程都去在鏈表中插入,比如都在做尾插,都在找尾巴,那么多線程就會出問題;

怎么解決這個問題呢?就是線程同步;

在一個多線程程序里,默認情況下,只有一個errno變量供所有的線程共享.在一個線程準備獲取剛才的錯誤代碼時,該變量很容易被另一個線程中的函數調用所改變.

2.線程同步的概念:

一個進程中的所有線程共享同一個地址空間和諸如打開的文件之類的其他資源.一個線程對資源的任何修改都會影響同一個進程中其他線程的環境.因此,需要同步各種線程的活動,以便它們互不干涉且不破壞數據結構.例如,如果兩個線程都試圖同時往一個雙向鏈表中增加一個元素,則可能會丟失一個元素或者破壞鏈表結構.

同步就是讓所有線程按照一定的規則執行,使得其正確性和效率都有跡可循.線程同步的手段就是對線程之間的穿插進行控制.

線程同步指的是當一個線程在對某個臨界資源進行操作時,其他線程都不可以對這個資源進行操作,直到該線程完成操作, 其他線程才能操作,也就是協同步調,讓線程按預定的先后次序進行運行。

臨界資源:同一時刻,只允許被一個進程或者線程訪問的資源;(比如打印機)

臨界區:訪問臨界資源的代碼段;

線程同步的方法有四種:互斥鎖、信號量、條件變量、讀寫鎖.

3.互斥鎖:線程同步方法一

(1)互斥鎖接口

int pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t *attr);
attr:鎖的屬性,不需要傳空即可int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_unlock(pthread_mutex_t *mutex);int pthread_mutex_destroy(pthread_mutex_t *mutex);   
//注意,互斥鎖mutex都需要傳地址,因為要改變它;

(2)互斥鎖例1(解決多線程并發問題)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <pthread.h>pthread_mutex_t  mutex;
int wg=0;
void *fun(void *arg)
{for(int i=0;i<1000;i++){pthread_mutex_lock(&mutex);wg++;pthread_mutex_unlock(&mutex);printf("wg=%d\n",wg);}
}
int main()
{pthread_t id[5];pthread_mutex_init(&mutex,NULL);for(int i=0;i<5;i++){pthread_create(&id[i],NULL,fun,NULL);}for(int i=0;i<5;i++){pthread_join(id[i],NULL);}pthread_mutex_destroy(&mutex);exit(0);
}

?

(3)互斥鎖例2(共享資源(打印機)使用問題)

主線程和函數線程模擬訪問打印機,主線程輸出第一個字符‘ A’表示開始使用打印機,輸出第二個字符‘ A’表示結束使用,函數線程操作與主線程相同。

(由于打印機同一時刻只能被一個線程使用,所以輸出結果不應該出現ABAB交替出現) :

原來是用信號量進行控制的,我們這里也可以用互斥鎖進行同步;

示例代碼:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>pthread_mutex_t mutex;void *thread_fun(void *arg)
{int i=0;for(;i<5;i++){pthread_mutex_lock(&mutex);write(1,"B",1);int n=rand()%3;sleep(n);write(1,"B",1);pthread_mutex_unlock(&mutex);n=rand()%3;sleep(n);}pthread_exit(NULL);
}int main()
{pthread_t id;pthread_mutex_init(&mutex,NULL);pthread_create(&id,NULL,thread_fun,NULL);int i=0;for(;i<5;i++){pthread_mutex_lock(&mutex);write(1,"A",1);int n=rand()%3;sleep(n);write(1,"A",1);pthread_mutex_unlock(&mutex);n=rand()%3;sleep(n);}pthread_join(id,NULL);pthread_mutex_destroy(&mutex);exit(0);
}

4.信號量(線程)線程同步方法二

(1)信號量接口

信號量的類型:
sem_t  全局定義一個sem_t類型的信號量
注意,必須要加頭文件:#include <semaphore.h>int sem_init(sem_t *sem, int pshared, unsigned int value);
//信號量的初始化
//sem_init()在sem指向的地址初始化未命名的信號量。這個value參數指定信號量的初始值(第三個參數)。
//pshared:設置信號量是否在進程間共享,Linux不支持,一般給0; (非0為共享)int sem_wait(sem_t *sem);
//P操作,wait表示等待,相當于是等待獲取資源,那么就是P操作int sem_post(sem_t *sem);
//V操作int sem_destroy(sem_t *sem);  
//銷毀信號量

(2)信號量例1(全局變量++正確性問題)

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <semaphore.h>
#include <pthread.h>sem_t sem;
int wg=0;
void *fun(void *arg)
{for(int i=0;i<1000;i++){sem_wait(&sem);wg++;sem_post(&sem);printf("wg=%d\n",wg);}
}
int main()
{pthread_t id[5];sem_init(&sem,0,1);for(int i=0;i<5;i++){pthread_create(&id[i],NULL,fun,NULL);}for(int i=0;i<5;i++){pthread_join(id[i],NULL);}sem_destroy(&sem);exit(0);
}

(3)信號量例2

主線程獲取用戶輸入,函數線程將用戶輸入的數據存儲到文件中;

//semtest.c
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <fcntl.h>char buff[128]={0};
//主線程完成獲取用戶數據的數據,并存儲在全局數組buff中;sem_t sem1;
sem_t sem2;void *PthreadFun(void *arg)
{int fd=open("a.txt",O_RDWR|O_CREAT,0664);//O_RDWR:讀寫方式打開assert(fd!=-1);//函數線程完成將用戶輸入的數據存儲到文件中while(1){sem_wait(&sem2);if(strncmp(buff,"end",3)==0){break;}write(fd,buff,strlen(buff));memset(buff,0,128);sem_post(&sem1);}sem_destroy(&sem1);sem_destroy(&sem2);
}int main()
{sem_init(&sem1,0,1);sem_init(&sem2,0,0);pthread_t id;int res=pthread_create(&id,NULL,PthreadFun,NULL);assert(res==0);//主線程完成獲取用戶數據的數據,并存儲在全局數組buff中;while(1){sem_wait(&sem1);printf("please input data:");fflush(stdout);fgets(buff,128,stdin);buff[strlen(buff)-1]=0;sem_post(&sem2);if(strncmp(buff,"end",3)==0){break;}}pthread_exit(NULL);
}

(3)信號量例3

(以前是用多進程做的,封裝比較麻煩.那如果用多線程的信號量做,如何做呢)

剛開始先打印A,所以信號量為1,后面的B和C不能打印,所以后面兩個信號量的初始值都為0;

?上面是多進程的思路圖,多線程的思路和多進程是一樣的.

不使用信號量,代碼如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>void * funa(void *arg)
{for(int i=0;i<5;i++){printf("A");fflush(stdout);sleep(1);//這個其實不需要的,只是為了便于觀察程序的執行;}
}void * funb(void *arg)
{for(int i=0;i<5;i++){printf("B");fflush(stdout);sleep(1);//這個其實不需要的,只是為了便于觀察程序的執行;}
}void * func(void *arg)
{for(int i=0;i<5;i++){printf("C");fflush(stdout);sleep(1);//這個其實不需要的,只是為了便于觀察程序的執行;}
}int main()
{pthread_t id[3];pthread_create(&id[0],NULL,funa,NULL);pthread_create(&id[1],NULL,funb,NULL);pthread_create(&id[2],NULL,func,NULL);for(int i=0;i<3;i++){pthread_join(id[i],NULL);}exit(0);}

?執行三次,三次不一樣的結果

?如何使用信號量呢?

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>//加入1-15 代碼
sem_t sema;//1
sem_t semb;//2
sem_t semc;//3void * funa(void *arg)
{for(int i=0;i<5;i++){sem_wait(&sema);//4相當于圖上的ps1;printf("A");fflush(stdout);sem_post(&semb);//5相當于圖上的vs2;sleep(1);//這個其實不需要的,只是為了便于觀察程序的執行;}
}void * funb(void *arg)
{for(int i=0;i<5;i++){sem_wait(&semb);//6相當于圖上的ps2;printf("B");fflush(stdout);sem_post(&semc);//7相當于圖上的vs3;sleep(1);//這個其實不需要的,只是為了便于觀察程序的執行;}
}void * func(void *arg)
{for(int i=0;i<5;i++){sem_wait(&semc);//8相當于圖上的ps3;printf("C");fflush(stdout);sem_post(&sema);//9相當于圖上的vs1;sleep(1);//這個其實不需要的,只是為了便于觀察程序的執行;}
}int main()
{sem_init(&sema,0,1);//10也可以這么理解,第二個參數一直為0,因為不能在進程間共享;sem_init(&semb,0,0);//11sem_init(&semc,0,0);//12pthread_t id[3];pthread_create(&id[0],NULL,funa,NULL);pthread_create(&id[1],NULL,funb,NULL);pthread_create(&id[2],NULL,func,NULL);for(int i=0;i<3;i++){pthread_join(id[i],NULL);}sem_destroy(&sema);//13sem_destroy(&semb);//14sem_destroy(&semc);//15exit(0);}

?

5.條件變量(線程同步方法三)

1)條件變量的接口:

#include <pthread.h>int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *attr);int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
//將條件變量添加到等待隊列中,阻塞,等待被喚醒;第一個參數是條件變量的地址,第二個參數是互斥鎖;也就是說條件變量往往伴隨著互斥鎖的使用;int pthread_cond_signal(pthread_cond_t *cond); //喚醒單個線程int pthread_cond_broadcast(pthread_cond_t *cond); //喚醒所有等待的線程int pthread_cond_destroy(pthread_cond_t *cond);//銷毀條件變量

?例題:主函數輸入數據到全局變量buf中.

主線程負責從鍵盤獲取數據,獲取到數據之后把數據寫入到buff中,我們就認為條件滿足了,我們就去喚醒等待著的兩個線程,讓其中的某一個線程將buff中的數據打印出去;

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>pthread_cond_t cond;
pthread_mutex_t mutex;//條件變量一般伴隨著互斥鎖的使用;char buff[128]={0};void *work_thread(void *arg)
{char *thread_name=(char *)arg;while(1){pthread_mutex_lock(&mutex);pthread_cond_wait(&cond,&mutex);pthread_mutex_unlock(&mutex);if(strncmp(buff,"end",3)==0){break;//1}printf("%s  %s",thread_name,buff);//2}
}int main()
{pthread_cond_init(&cond,NULL);pthread_mutex_init(&mutex,NULL);pthread_t id1,id2;pthread_create(&id1,NULL,work_thread,"thread:1");pthread_create(&id2,NULL,work_thread,"thread:2");while(1){fgets(buff,128,stdin);if(strncmp(buff,"end",3)==0){pthread_cond_broadcast(&cond);break;}else{pthread_cond_signal(&cond);}}pthread_join(id1,NULL);pthread_join(id2,NULL);pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);printf("main over\n");exit(0);
}

?

pthread_cond_broadcast函數以廣播的方式喚醒所有等待目標條件變量的線程; pthread_cond_signal函數用于喚醒一個等待目標條件變量的線程.至于哪個線程將被喚醒,則取決于線程的優先級和調度策略.有時候我們可能想喚醒一個指定的線程,但是pthread沒有對該需求提供解決方法.

pthread_cond_wait函數用于等待目標條件變量.mutex參數是用于保護條件變量的互斥鎖,以確保pthread_conde_wait操作的原子性.在調用pthread_cond_wait前,必須確保互斥鎖mutex已經加鎖,否則將導致不可預期的結果.pthread_cond_wait函數執行時,首先把調用線程放入條件變量的等待隊列中,然后將互斥鎖mutex解鎖.可見,從pthread_cond_wait開始執行到其調用線程被放入條件變量的等待隊列之間的這段時間內,pthread_cond_signal和pthread_cond_broadcast等函數不會修改條件變量.換言之,pthread_cond_wait函數不會錯過目標條件變量的任何變化.當pthread_cond_wait函數成功返回時,互斥鎖mutex將再次被鎖上.

6.讀寫鎖(線程同步方法四)

讀寫鎖保證了更高的并發性;

讀寫鎖適用讀的場景比較多的場景;

#include <pthread.h>
int pthread_rwlock_init(pthread_rwlock_t *rwlock, pthread_rwlockattr_t *attr);//第一個參數是鎖的地址,第二個參數是鎖的屬性
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);//加讀鎖
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);//加寫鎖
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);//解鎖,不管加的讀鎖還是寫鎖,都是通過unlock解鎖;
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);//銷毀  //讀模式下的加鎖狀態,寫模式下的加鎖狀態,不加鎖的狀態

///模擬 //我們可以創建3個線程,兩個線程進行讀操作,一個線程進行寫操作; //我們模擬一下這個,可以讓兩個讀操作同時操作;但是讀和寫是不能同時操作的;

我們首先來看沒有控制的代碼:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>pthread_rwlock_t rwlock;
void * fun_read(void * arg)
{char *s=(char *)arg;for(int i=0;i<5;i++){printf("%s:  start\n",s);int n=rand()%3;sleep(n);printf("%s:  end\n",s);n=rand()%3;sleep(n);}
}void * fun_write(void * arg)
{char *s=(char *)arg;for(int i=0;i<5;i++){printf("%s:  start\n",s);int n=rand()%3;sleep(n);printf("%s:  end\n",s);n=rand()%3;sleep(n);}
}int main()
{pthread_rwlock_init(&rwlock,NULL);pthread_t id1,id2,id3;pthread_create(&id1,NULL,fun_read,"第一個讀線程:");pthread_create(&id2,NULL,fun_read,"第二個讀線程:");pthread_create(&id3,NULL,fun_write,"第一個寫線程:");pthread_join(id1,NULL);pthread_join(id2,NULL);pthread_join(id3,NULL);pthread_rwlock_destroy(&rwlock);exit(0);}

?

如何運用讀寫鎖呢?代碼如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>pthread_rwlock_t rwlock;
void * fun_read(void * arg)
{char *s=(char *)arg;for(int i=0;i<5;i++){pthread_rwlock_rdlock(&rwlock);//1pthread_rwlock_rdlock命名含有:線程的_讀寫鎖_讀鎖;printf("%s:  start\n",s);int n=rand()%3;sleep(n);printf("%s:  end\n",s);pthread_rwlock_unlock(&rwlock);//2pthread_rwlock_unlock命名含有:線程的_讀寫鎖_解鎖;n=rand()%3;sleep(n);}
}void * fun_write(void * arg)
{char *s=(char *)arg;for(int i=0;i<5;i++){pthread_rwlock_wrlock(&rwlock);//3pthread_rwlock_rwlock命名含有:線程的_讀寫鎖_寫鎖;printf("%s:  start\n",s);int n=rand()%3;sleep(n);printf("%s:  end\n",s);pthread_rwlock_unlock(&rwlock);//4pthread_rwlock_unlock命名含有:線程的_讀寫鎖_解鎖;n=rand()%3;sleep(n);}
}int main()
{pthread_rwlock_init(&rwlock,NULL);pthread_t id1,id2,id3;pthread_create(&id1,NULL,fun_read,"r1");pthread_create(&id2,NULL,fun_read,"r2");pthread_create(&id3,NULL,fun_write,"w1");pthread_join(id1,NULL);pthread_join(id2,NULL);pthread_join(id3,NULL);pthread_rwlock_destroy(&rwlock);exit(0);}

?

?我們要保證寫的start和end中間是不能出現讀的,而且讀兩個可以,讀的中間不能有寫;

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

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

相關文章

寫.NET可以指定運行SUB MAIN嗎?調用任意一個里面的類時,如何先執行某段初始化代碼?

VB.NET 寫.NET可以指定運行SUB MAIN嗎?調用任意一個里面的類時,如何先執行某段初始化代碼? 分享 1. 在 VB.NET 中指定運行 Sub Main 在 VB.NET 里&#xff0c;你能夠指定 Sub Main 作為程序的入口點。下面為你介紹兩種實現方式&#xff1a; 方式一&#xff1a;在項目屬性…

【AI插件開發】Notepad++ AI插件開發實踐(代碼篇):從Dock窗口集成到功能菜單實現

一、引言 上篇文章已經在Notepad的插件開發中集成了選中即問AI的功能&#xff0c;這一篇文章將在此基礎上進一步集成&#xff0c;支持AI對話窗口以及常見的代碼功能菜單&#xff1a; 顯示AI的Dock窗口&#xff0c;可以用自然語言向 AI 提問或要求執行任務選中代碼后使用&…

關聯容器-模板類pair數對

關聯容器 關聯容器和順序容器有著根本的不同:關聯容器中的元素是按關鍵字來保存和訪問的,而順序容器中的元素是按它們在容器中的位置來順序保存和訪問的。 關聯容器支持高效的關鍵字查找和訪問。 兩個主要的關聯容器(associative-container),set和map。 set 中每個元素只包…

京東運維面試題及參考答案

目錄 OSPF 實現原理是什么? 請描述 TCP 三次握手的過程。 LVS 的原理是什么? 闡述 Nginx 七層負載均衡的原理。 Nginx 與 Apache 有什么區別? 如何查看監聽在 8080 端口的是哪個進程(可舉例:netstat -tnlp | grep 8080)? OSI 七層模型是什么,請寫出各層的協議。 …

輸入框輸入數字且保持精度

在項目中如果涉及到金額等需要數字輸入且保持精度的情況下&#xff0c;由于輸入框是可以隨意輸入文本的&#xff0c;所以一般情況下可能需要監聽輸入框的change事件&#xff0c;然后通過正則表達式去替換掉不匹配的文本部分。 由于每次文本改變都會被監聽&#xff0c;包括替換…

使用 requests 和 BeautifulSoup 解析淘寶商品

以下將詳細解釋如何通過這兩個庫來實現按關鍵字搜索并解析淘寶商品信息。 一、準備工作 1. 安裝必要的庫 在開始之前&#xff0c;確保已經安裝了 requests 和 BeautifulSoup 庫。如果尚未安裝&#xff0c;可以通過以下命令進行安裝&#xff1a; bash pip install requests…

C#調用ACCESS數據庫,解決“Microsoft.ACE.OLEDB.12.0”未注冊問題

C#調用ACCESS數據庫&#xff0c;解決“Microsoft.ACE.OLEDB.12.0”未注冊問題 解決方法&#xff1a; 1.將C#采用的平臺從AnyCpu改成X64 2.將官網下載的“Microsoft Access 2010 數據庫引擎可再發行程序包AccessDatabaseEngine_X64”文件解壓 3.安裝解壓后的文件 點擊下載安…

【文獻閱讀】Vision-Language Models for Vision Tasks: A Survey

發表于2024年2月 TPAMI 摘要 大多數視覺識別研究在深度神經網絡&#xff08;DNN&#xff09;訓練中嚴重依賴標注數據&#xff0c;并且通常為每個單一視覺識別任務訓練一個DNN&#xff0c;這導致了一種費力且耗時的視覺識別范式。為應對這兩個挑戰&#xff0c;視覺語言模型&am…

【Kubernetes】StorageClass 的作用是什么?如何實現動態存儲供應?

StorageClass 使得用戶能夠根據不同的存儲需求動態地申請和管理存儲資源。 StorageClass 定義了如何創建存儲資源&#xff0c;并指定了存儲供應的配置&#xff0c;例如存儲類型、質量、訪問模式等。為動態存儲供應提供了基礎&#xff0c;使得 Kubernetes 可以在用戶創建 PVC 時…

Muduo網絡庫介紹

1.Reactor介紹 1.回調函數 **回調&#xff08;Callback&#xff09;**是一種編程技術&#xff0c;允許將一個函數作為參數傳遞給另一個函數&#xff0c;并在適當的時候調用該函數 1.工作原理 定義回調函數 注冊回調函數 觸發回調 2.優點 異步編程 回調函數允許在事件發生時…

Debian編譯安裝mysql8.0.41源碼包 筆記250401

Debian編譯安裝mysql8.0.41源碼包 以下是在Debian系統上通過編譯源碼安裝MySQL 8.0.41的完整步驟&#xff0c;包含依賴管理、編譯參數優化和常見問題處理&#xff1a; 準備工作 1. 安裝編譯依賴 sudo apt update sudo apt install -y \cmake gcc g make libssl-dev …

Git常用問題收集

gitignore 忽略文件夾 不生效 有時候我們接手別人的項目時&#xff0c;發現有的忽略不對想要修改&#xff0c;但發現修改忽略.gitignore后無效。原因是如果某些文件已經被納入版本管理在.gitignore中忽略路徑是不起作用的&#xff0c;這時候需要先清除本地緩存&#xff0c;然后…

編程哲學——TCP可靠傳輸

TCP TCP可靠傳輸 TCP的可靠傳輸表現在 &#xff08;1&#xff09;建立連接時三次握手&#xff0c;四次揮手 有點像是這樣對話&#xff1a; ”我們開始對話吧“ ”收到“ ”好的&#xff0c;我收到你收到了“ &#xff08;2&#xff09;數據傳輸時ACK應答和超時重傳 ”我們去吃…

【MediaPlayer】基于libvlc+awtk的媒體播放器

基于libvlcawtk的媒體播放器 libvlc下載地址 awtk下載地址 代碼實現libvlc相關邏輯接口UI媒體接口實例化媒體播放器注意事項 libvlc 下載地址 可以到https://download.videolan.org/pub/videolan/vlc/去下載一個vlc版本&#xff0c;下載后其實是vlc的windows客戶端&#xff0…

pulsar中的延遲隊列使用詳解

Apache Pulsar的延遲隊列支持任意時間精度的延遲消息投遞&#xff0c;適用于金融交易、定時提醒等高時效性場景。其核心設計通過堆外內存索引隊列與持久化分片存儲實現&#xff0c;兼顧靈活性與可擴展性。以下從實現原理、使用方式、優化策略及挑戰展開解析&#xff1a; 一、核…

單鏈表的實現 | 附學生信息管理系統的實現

目錄 1.前言&#xff1a; 2.單鏈表的相關概念&#xff1a; 2.1定義&#xff1a; 2.2形式&#xff1a; 2.3特點&#xff1a; 3.常見功能及代碼 &#xff1a; 3.1創建節點&#xff1a; 3.2頭插&#xff1a; 3.3尾插&#xff1a; 3.4頭刪&#xff1a; 3.5尾刪&#xff1a; 3.6插入…

java實用工具類Localstorage

public class LocalStorageUtil {//提供ThreadLocal對象,private static ThreadLocal threadLocalnew ThreadLocal();public static Object get(){return threadLocal.get();}public static void set(Object o){threadLocal.set(o);}public static void remove(){threadLocal.r…

LLM-大語言模型淺談

目錄 核心定義 典型代表 核心原理 用途 優勢與局限 未來發展方向 LLM&#xff08;Large Language Model&#xff09;大語言模型&#xff0c;指通過海量文本數據訓練 能夠理解和生成人類語言的深度學習模型。 核心定義 一種基于深度神經網絡&#xff08;如Transformer架…

【小兔鮮】day03 Home模塊與一級分類

【小兔鮮】day03 Home模塊與一級分類 1. Home-整體結構搭建和分類實現1.1 頁面結構 2. Home-banner輪播圖功能實現 1. Home-整體結構搭建和分類實現 1.1 頁面結構 分類實現 2. Home-banner輪播圖功能實現 輪播圖實現 在HomeBanner.vue中寫出輪播圖的結構 在apis目錄下新建h…

C++中的多態和模板

#include <iostream> #include <cstdlib> #include <ctime> #include <string>using namespace std;// 武器基類 class Weapon { public:virtual ~Weapon() {}virtual string getName() const 0; // 獲取武器名稱virtual int getAtk() const 0; …