中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

招聘企業(yè)網(wǎng)站建設(shè)模塊關(guān)鍵詞排名優(yōu)化是什么意思

招聘企業(yè)網(wǎng)站建設(shè)模塊,關(guān)鍵詞排名優(yōu)化是什么意思,電腦上怎么使用wordpress,用php做的網(wǎng)站源代碼文章目錄1.干貨寫在前面2.ucontext初接觸3.ucontext組件到底是什么4.小試牛刀-使用ucontext組件實(shí)現(xiàn)線程切換5.使用ucontext實(shí)現(xiàn)自己的線程庫6.最后一步-使用我們自己的協(xié)程庫1.干貨寫在前面 協(xié)程是一種用戶態(tài)的輕量級(jí)線程 首先我們可以看看有哪些語言已經(jīng)具備協(xié)程語義&#x…

文章目錄

    • 1.干貨寫在前面
    • 2.ucontext初接觸
    • 3.ucontext組件到底是什么
    • 4.小試牛刀-使用ucontext組件實(shí)現(xiàn)線程切換
    • 5.使用ucontext實(shí)現(xiàn)自己的線程庫
    • 6.最后一步-使用我們自己的協(xié)程庫

1.干貨寫在前面

  • 協(xié)程是一種用戶態(tài)的輕量級(jí)線程

  • 首先我們可以看看有哪些語言已經(jīng)具備協(xié)程語義:

    • 比較重量級(jí)的有C#、erlang、golang*

    • 輕量級(jí)有python、lua、javascript、ruby

    • 還有函數(shù)式的scala、scheme等

      c/c++不直接支持協(xié)程語義,但有不少開源的協(xié)程庫,如:
      Protothreads:一個(gè)“蠅量級(jí)” C 語言協(xié)程庫
      libco:來自騰訊的開源協(xié)程庫libco介紹,官網(wǎng)
      coroutine:云風(fēng)的一個(gè)C語言同步協(xié)程庫,詳細(xì)信息

  • 目前看到大概有四種實(shí)現(xiàn)協(xié)程的方式:

    • 第一種:利用glibc 的 ucontext組件(云風(fēng)的庫)

    • 第二種:使用匯編代碼來切換上下文(實(shí)現(xiàn)c協(xié)程)

    • 第三種:利用C語言語法switch-case的奇淫技巧來實(shí)現(xiàn)(Protothreads)

    • 第四種:利用了 C 語言的 setjmp 和 longjmp( 一種協(xié)程的 C/C++ 實(shí)現(xiàn),要求函數(shù)里面使用 static local 的變量來保存協(xié)程內(nèi)部的數(shù)據(jù))

      本篇主要使用ucontext來實(shí)現(xiàn)簡單的協(xié)程庫。

2.ucontext初接觸

利用ucontext提供的四個(gè)函數(shù)getcontext(),setcontext(),makecontext(),swapcontext()可以在一個(gè)進(jìn)程中實(shí)現(xiàn)用戶級(jí)的線程切換。

本節(jié)我們先來看ucontext實(shí)現(xiàn)的一個(gè)簡單的例子:

#include <stdio.h>
#include <ucontext.h>
#include <unistd.h>int main(int argc, const char *argv[]){ucontext_t context;getcontext(&context);puts("Hello world");sleep(1);setcontext(&context);return 0;
}

注:示例代碼來自維基百科.

保存上述代碼到example.c,執(zhí)行編譯命令:

gcc example.c -o example

想想程序運(yùn)行的結(jié)果會(huì)是什么樣?

cxy@ubuntu:~$ ./example 
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
^C
cxy@ubuntu:~$

上面是程序執(zhí)行的部分輸出,不知道是否和你想得一樣呢?我們可以看到,程序在輸出第一個(gè)“Hello world"后并沒有退出程序,而是持續(xù)不斷的輸出”Hello world“。其實(shí)是程序通過getcontext先保存了一個(gè)上下文,然后輸出"Hello world",在通過setcontext恢復(fù)到getcontext的地方,重新執(zhí)行代碼,所以導(dǎo)致程序不斷的輸出”Hello world“,在我這個(gè)菜鳥的眼里,這簡直就是一個(gè)神奇的跳轉(zhuǎn)。
那么問題來了,ucontext到底是什么?

3.ucontext組件到底是什么

在類System V環(huán)境中,在頭文件< ucontext.h > 中定義了兩個(gè)結(jié)構(gòu)類型,mcontext_t和ucontext_t和四個(gè)函數(shù)getcontext(),setcontext(),makecontext(),swapcontext().利用它們可以在一個(gè)進(jìn)程中實(shí)現(xiàn)用戶級(jí)的線程切換。

mcontext_t類型與機(jī)器相關(guān),并且不透明.ucontext_t結(jié)構(gòu)體則至少擁有以下幾個(gè)域:

  typedef struct ucontext {struct ucontext *uc_link;sigset_t         uc_sigmask;stack_t          uc_stack;mcontext_t       uc_mcontext;...} ucontext_t;

當(dāng)當(dāng)前上下文(如使用makecontext創(chuàng)建的上下文)運(yùn)行終止時(shí)系統(tǒng)會(huì)恢復(fù)uc_link指向的上下文;uc_sigmask為該上下文中的阻塞信號(hào)集合;uc_stack為該上下文中使用的棧;uc_mcontext保存的上下文的特定機(jī)器表示,包括調(diào)用線程的特定寄存器等。

下面詳細(xì)介紹四個(gè)函數(shù):

int getcontext(ucontext_t *ucp);

初始化ucp結(jié)構(gòu)體,將當(dāng)前的上下文保存到ucp中

int setcontext(const ucontext_t *ucp);

設(shè)置當(dāng)前的上下文為ucp,setcontext的上下文ucp應(yīng)該通過getcontext或者makecontext取得,如果調(diào)用成功則不返回。如果上下文是通過調(diào)用getcontext()取得,程序會(huì)繼續(xù)執(zhí)行這個(gè)調(diào)用。如果上下文是通過調(diào)用makecontext取得,程序會(huì)調(diào)用makecontext函數(shù)的第二個(gè)參數(shù)指向的函數(shù),如果func函數(shù)返回,則恢復(fù)makecontext第一個(gè)參數(shù)指向的上下文第一個(gè)參數(shù)指向的上下文context_t中指向的uc_link.如果uc_link為NULL,則線程退出。

void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...);

makecontext修改通過getcontext取得的上下文ucp(這意味著調(diào)用makecontext前必須先調(diào)用getcontext)。然后給該上下文指定一個(gè)??臻gucp->stack,設(shè)置后繼的上下文ucp->uc_link.

當(dāng)上下文通過setcontext或者swapcontext激活后,執(zhí)行func函數(shù),argc為func的參數(shù)個(gè)數(shù),后面是func的參數(shù)序列。當(dāng)func執(zhí)行返回后,繼承的上下文被激活,如果繼承上下文為NULL時(shí),線程退出。

int swapcontext(ucontext_t *oucp, ucontext_t *ucp);

保存當(dāng)前上下文到oucp結(jié)構(gòu)體中,然后激活upc上下文。

如果執(zhí)行成功,getcontext返回0,setcontext和swapcontext不返回;如果執(zhí)行失敗,getcontext,setcontext,swapcontext返回-1,并設(shè)置對(duì)于的errno.

簡單說來, getcontext獲取當(dāng)前上下文,setcontext設(shè)置當(dāng)前上下文,swapcontext切換上下文,makecontext創(chuàng)建一個(gè)新的上下文。

4.小試牛刀-使用ucontext組件實(shí)現(xiàn)線程切換

雖然我們稱協(xié)程是一個(gè)用戶態(tài)的輕量級(jí)線程,但實(shí)際上多個(gè)協(xié)程同屬一個(gè)線程。任意一個(gè)時(shí)刻,同一個(gè)線程不可能同時(shí)運(yùn)行兩個(gè)協(xié)程。如果我們將協(xié)程的調(diào)度簡化為:主函數(shù)調(diào)用協(xié)程1,運(yùn)行協(xié)程1直到協(xié)程1返回主函數(shù),主函數(shù)在調(diào)用協(xié)程2,運(yùn)行協(xié)程2直到協(xié)程2返回主函數(shù)。示意步驟如下:

執(zhí)行主函數(shù)
切換:主函數(shù) --> 協(xié)程1
執(zhí)行協(xié)程1
切換:協(xié)程1  --> 主函數(shù)
執(zhí)行主函數(shù)
切換:主函數(shù) --> 協(xié)程2
執(zhí)行協(xié)程2
切換協(xié)程2  --> 主函數(shù)
執(zhí)行主函數(shù)
...

這種設(shè)計(jì)的關(guān)鍵在于實(shí)現(xiàn)主函數(shù)到一個(gè)協(xié)程的切換,然后從協(xié)程返回主函數(shù)。這樣無論是一個(gè)協(xié)程還是多個(gè)協(xié)程都能夠完成與主函數(shù)的切換,從而實(shí)現(xiàn)協(xié)程的調(diào)度。
實(shí)現(xiàn)用戶線程的過程是:

  1. 我們首先調(diào)用getcontext獲得當(dāng)前上下文
  2. 修改當(dāng)前上下文ucontext_t來指定新的上下文,如指定棧空間極其大小,設(shè)置用戶線程執(zhí)行完后返回的后繼上下文(即主函數(shù)的上下文)等
  3. 調(diào)用makecontext創(chuàng)建上下文,并指定用戶線程中要執(zhí)行的函數(shù)
  4. 切換到用戶線程上下文去執(zhí)行用戶線程(如果設(shè)置的后繼上下文為主函數(shù),則用戶線程執(zhí)行完后會(huì)自動(dòng)返回主函數(shù))。
    下面代碼context_test函數(shù)完成了上面的要求。
#include <ucontext.h>
#include <stdio.h>void func1(void * arg)
{puts("1");puts("11");puts("111");puts("1111");}
void context_test()
{char stack[1024*128];ucontext_t child,main;getcontext(&child); //獲取當(dāng)前上下文child.uc_stack.ss_sp = stack;//指定??臻gchild.uc_stack.ss_size = sizeof(stack);//指定棧空間大小child.uc_stack.ss_flags = 0;child.uc_link = &main;//設(shè)置后繼上下文makecontext(&child,(void (*)(void))func1,0);//修改上下文指向func1函數(shù)swapcontext(&main,&child);//切換到child上下文,保存當(dāng)前上下文到mainputs("main");//如果設(shè)置了后繼上下文,func1函數(shù)指向完后會(huì)返回此處
}int main()
{context_test();return 0;
}

在context_test中,創(chuàng)建了一個(gè)用戶線程child,其運(yùn)行的函數(shù)為func1.指定后繼上下文為main
func1返回后激活后繼上下文,繼續(xù)執(zhí)行主函數(shù)。

保存上面代碼到example-switch.cpp.運(yùn)行編譯命令:

g++ example-switch.cpp -o example-switch

執(zhí)行程序結(jié)果如下

cxy@ubuntu:~$ ./example-switch
1
11
111
1111
main
cxy@ubuntu:~$

你也可以通過修改后繼上下文的設(shè)置,來觀察程序的行為。如修改代碼

child.uc_link = &main;

child.uc_link = NULL;

再重新編譯執(zhí)行,其執(zhí)行結(jié)果為:

cxy@ubuntu:~$ ./example-switch
1
11
111
1111
cxy@ubuntu:~$

可以發(fā)現(xiàn)程序沒有打印"main",執(zhí)行為func1后直接退出,而沒有返回主函數(shù)??梢?#xff0c;如果要實(shí)現(xiàn)主函數(shù)到線程的切換并返回,指定后繼上下文是非常重要的。

5.使用ucontext實(shí)現(xiàn)自己的線程庫

掌握了上一節(jié)從主函數(shù)到協(xié)程的切換的關(guān)鍵,我們就可以開始考慮實(shí)現(xiàn)自己的協(xié)程了。
定義一個(gè)協(xié)程的結(jié)構(gòu)體如下:

typedef void (*Fun)(void *arg);typedef struct uthread_t
{ucontext_t ctx;Fun func;void *arg;enum ThreadState state;char stack[DEFAULT_STACK_SZIE];
}uthread_t;

ctx保存協(xié)程的上下文,stack為協(xié)程的棧,棧大小默認(rèn)為DEFAULT_STACK_SZIE=128Kb.你可以根據(jù)自己的需求更改棧的大小。func為協(xié)程執(zhí)行的用戶函數(shù),arg為func的參數(shù),state表示協(xié)程的運(yùn)行狀態(tài),包括FREE,RUNNABLE,RUNING,SUSPEND,分別表示空閑,就緒,正在執(zhí)行和掛起四種狀態(tài)。
在定義一個(gè)調(diào)度器的結(jié)構(gòu)體

typedef std::vector<uthread_t> Thread_vector;typedef struct schedule_t
{ucontext_t main;int running_thread;Thread_vector threads;schedule_t():running_thread(-1){}
}schedule_t;

調(diào)度器包括主函數(shù)的上下文main,包含當(dāng)前調(diào)度器擁有的所有協(xié)程的vector類型的threads,以及指向當(dāng)前正在執(zhí)行的協(xié)程的編號(hào)running_thread.如果當(dāng)前沒有正在執(zhí)行的協(xié)程時(shí),running_thread=-1.
接下來,在定義幾個(gè)使用函數(shù)uthread_create,uthread_yield,uthread_resume函數(shù)已經(jīng)輔助函數(shù)schedule_finished.就可以了。

int  uthread_create(schedule_t &schedule,Fun func,void *arg);

創(chuàng)建一個(gè)協(xié)程,該協(xié)程的會(huì)加入到schedule的協(xié)程序列中,func為其執(zhí)行的函數(shù),arg為func的執(zhí)行函數(shù)。返回創(chuàng)建的線程在schedule中的編號(hào)。

void uthread_yield(schedule_t &schedule);

掛起調(diào)度器schedule中當(dāng)前正在執(zhí)行的協(xié)程,切換到主函數(shù)。

void uthread_resume(schedule_t &schedule,int id);

恢復(fù)運(yùn)行調(diào)度器schedule中編號(hào)為id的協(xié)程

int  schedule_finished(const schedule_t &schedule);

判斷schedule中所有的協(xié)程是否都執(zhí)行完畢,是返回1,否則返回0.注意:如果有協(xié)程處于掛起狀態(tài)時(shí)算作未全部執(zhí)行完畢,返回0.

代碼就不全貼出來了,我們來看看兩個(gè)關(guān)鍵的函數(shù)的具體實(shí)現(xiàn)。首先是uthread_resume函數(shù):

void uthread_resume(schedule_t &schedule , int id)
{if(id < 0 || id >= schedule.threads.size()){return;}uthread_t *t = &(schedule.threads[id]);switch(t->state){case RUNNABLE:getcontext(&(t->ctx));t->ctx.uc_stack.ss_sp = t->stack;t->ctx.uc_stack.ss_size = DEFAULT_STACK_SZIE;t->ctx.uc_stack.ss_flags = 0;t->ctx.uc_link = &(schedule.main);t->state = RUNNING;schedule.running_thread = id;makecontext(&(t->ctx),(void (*)(void))(uthread_body),1,&schedule);/* !! note : Here does not need to break */case SUSPEND:swapcontext(&(schedule.main),&(t->ctx));break;default: ;}
}

如果指定的協(xié)程是首次運(yùn)行,處于RUNNABLE狀態(tài),則創(chuàng)建一個(gè)上下文,然后切換到該上下文。如果指定的協(xié)程已經(jīng)運(yùn)行過,處于SUSPEND狀態(tài),則直接切換到該上下文即可。代碼中需要注意RUNNBALE狀態(tài)的地方不需要break.

void uthread_yield(schedule_t &schedule)
{if(schedule.running_thread != -1 ){uthread_t *t = &(schedule.threads[schedule.running_thread]);t->state = SUSPEND;schedule.running_thread = -1;swapcontext(&(t->ctx),&(schedule.main));}
}

uthread_yield掛起當(dāng)前正在運(yùn)行的協(xié)程。首先是將running_thread置為-1,將正在運(yùn)行的協(xié)程的狀態(tài)置為SUSPEND,最后切換到主函數(shù)上下文。

6.最后一步-使用我們自己的協(xié)程庫

保存下面代碼到example-uthread.cpp.

#include "uthread.h"
#include <stdio.h>void func2(void * arg)
{puts("22");puts("22");uthread_yield(*(schedule_t *)arg);puts("22");puts("22");
}void func3(void *arg)
{puts("3333");puts("3333");uthread_yield(*(schedule_t *)arg);puts("3333");puts("3333");}void schedule_test()
{schedule_t s;int id1 = uthread_create(s,func3,&s);int id2 = uthread_create(s,func2,&s);while(!schedule_finished(s)){uthread_resume(s,id2);uthread_resume(s,id1);}puts("main over");}
int main()
{schedule_test();return 0;
}

執(zhí)行編譯命令并運(yùn)行:

g++ example-uthread.cpp -o example-uthread
./example-uthread

運(yùn)行結(jié)果如下:

cxy@ubuntu:~/mythread$./example-uthread
22
22
3333
3333
22
22
3333
3333
main over
cxy@ubuntu:~/mythread$

可以看到,程序協(xié)程func2,然后切換到主函數(shù),在執(zhí)行協(xié)程func3,再切換到主函數(shù),又切換到func2,在切換到主函數(shù),再切換到func3,最后切換到主函數(shù)結(jié)束。
總結(jié)一下,我們利用getcontext和makecontext創(chuàng)建上下文,設(shè)置后繼的上下文到主函數(shù),設(shè)置每個(gè)協(xié)程的棧空間。在利用swapcontext在主函數(shù)和協(xié)程之間進(jìn)行切換。

到此,使用ucontext做一個(gè)自己的協(xié)程庫就到此結(jié)束了。相信你也可以自己完成自己的協(xié)程庫了。

http://www.risenshineclean.com/news/42819.html

相關(guān)文章:

  • 南通旅游網(wǎng)站建設(shè)一鍵免費(fèi)建站
  • 搜索網(wǎng)站開發(fā)背景廣告留電話號(hào)的網(wǎng)站
  • 目前做批發(fā)比較好的b2b網(wǎng)站百度免費(fèi)發(fā)布信息
  • 創(chuàng)建視頻網(wǎng)站昆明百度搜索排名優(yōu)化
  • 織夢高端html5網(wǎng)站建設(shè)工作室網(wǎng)絡(luò)公司網(wǎng)站模板高端營銷型網(wǎng)站建設(shè)
  • 信陽做網(wǎng)站 漢獅網(wǎng)絡(luò)短視頻seo詢盤系統(tǒng)
  • 秦皇島做網(wǎng)站公司win10優(yōu)化大師怎么樣
  • 備案期間怎么做網(wǎng)站磁力
  • 石家莊網(wǎng)站建設(shè)策劃方案杭州seo搜索引擎優(yōu)化
  • 域名停域app免費(fèi)下載上海還能推seo嗎
  • 商城類的網(wǎng)站怎么做百度百度一下首頁
  • 做服裝設(shè)計(jì)兼職的網(wǎng)站短信廣告投放
  • 房地產(chǎn)型網(wǎng)站建設(shè)社區(qū)推廣方法有哪些
  • 做旅游網(wǎng)站官方百度下載安裝
  • 備案用網(wǎng)站建設(shè)方案書又一病毒來了比新冠可怕
  • 金華做網(wǎng)站報(bào)價(jià)seo關(guān)鍵字優(yōu)化軟件
  • 免費(fèi)網(wǎng)站建設(shè)作業(yè)總結(jié)seo的作用主要有
  • 做電子商務(wù)系統(tǒng)網(wǎng)站建設(shè)seo的搜索排名影響因素有哪些
  • 手機(jī)網(wǎng)站Com臺(tái)州做優(yōu)化
  • .net 網(wǎng)站開發(fā)視頻教程系統(tǒng)優(yōu)化軟件有哪些
  • 做外貿(mào)找產(chǎn)品上哪個(gè)網(wǎng)站好引流用什么話術(shù)更吸引人
  • t恤定制網(wǎng)站哪個(gè)好濟(jì)南seo網(wǎng)站優(yōu)化公司
  • 做藥公司的網(wǎng)站前置審批程序員培訓(xùn)機(jī)構(gòu)排名
  • 杭州做網(wǎng)站hzfwwl寧波seo網(wǎng)絡(luò)推廣代理公司
  • 用網(wǎng)站做淘寶客的人多嗎有哪些搜索引擎網(wǎng)站
  • 花園休閑平臺(tái)設(shè)計(jì)百度seo關(guān)鍵詞排名查詢工具
  • 建網(wǎng)站做站在網(wǎng)絡(luò)營銷的現(xiàn)狀和發(fā)展趨勢
  • 茂名網(wǎng)站建設(shè)托管專業(yè)競價(jià)托管哪家好
  • 網(wǎng)站建設(shè)總結(jié)報(bào)告營銷策劃公司經(jīng)營范圍
  • 太原做網(wǎng)站聯(lián)系方式近三天新聞50字左右