国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

使用C++20協(xié)程和io_uring優(yōu)雅地實(shí)現(xiàn)異步IO

這篇具有很好參考價(jià)值的文章主要介紹了使用C++20協(xié)程和io_uring優(yōu)雅地實(shí)現(xiàn)異步IO。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

距離2020年已經(jīng)過去很久了,各大編譯器對(duì)于C++20各項(xiàng)標(biāo)準(zhǔn)的支持也日趨完善,無棧協(xié)程也是其中之一,所以我就嘗試著拿協(xié)程與io_uring實(shí)現(xiàn)了一下proactor模式,這篇文章用來記錄一下我的設(shè)計(jì)和想法。除此之外,我們能在網(wǎng)絡(luò)上找到許多優(yōu)秀的C++20協(xié)程的教程以及許多優(yōu)秀的協(xié)程應(yīng)用(庫),但從協(xié)程入門到架構(gòu)出成熟的應(yīng)用(庫)之間還存在著不小的鴻溝,而直接去啃大型工程的源代碼絕對(duì)不算是一種高效率的學(xué)習(xí)方式。所以,如果這篇文章能夠在這方面提供一定的幫助的話那就再好不過了。

正如上所述,這篇文章是介紹基于C++20協(xié)程實(shí)現(xiàn)異步IO的,而不是介紹C++20協(xié)程的,因此有一定的閱讀門檻。在閱讀之前,你應(yīng)當(dāng)至少熟悉一下C++20協(xié)程。

為什么要使用協(xié)程

因?yàn)閰f(xié)程能夠讓我們像寫同步IO那樣來實(shí)現(xiàn)異步IO,如下所示:

auto foo(tcp_connection connection) -> task<void> {
    char buffer[1024];
    int result = co_await connection.recv(buffer, sizeof(buffer));
    // do something...
    result = co_await connection.send(buffer, result);
    // do something...
    co_return;
}

如果我們合理地實(shí)現(xiàn)了協(xié)程的掛起、恢復(fù)等操作,那么當(dāng)我們執(zhí)行co_await connection.recv時(shí),我們實(shí)際上希望代碼執(zhí)行的操作如下:

  1. 告訴操作系統(tǒng),監(jiān)聽recv操作,等待對(duì)方發(fā)送數(shù)據(jù);
  2. 掛起當(dāng)前協(xié)程;
  3. 去處理別的事情。

當(dāng)操作系統(tǒng)接收到recv的數(shù)據(jù)時(shí),執(zhí)行以下操作:

  1. 處理recv,把數(shù)據(jù)讀進(jìn)來;
  2. 恢復(fù)之前掛起的協(xié)程,從掛起的地方恢復(fù)執(zhí)行。

這就是我們需要協(xié)程做的事情。如果你熟悉reactor模式的話,這應(yīng)該并不陌生——我們只是把回調(diào)函數(shù)換成了協(xié)程而已。那么回到這一部分的標(biāo)題——我們?yōu)槭裁匆褂脜f(xié)程而不是回調(diào)函數(shù)呢?——因?yàn)槭褂脜f(xié)程寫出來的代碼更好看,也更好維護(hù),僅此而已。

關(guān)于為什么要使用異步IO:異步IO能夠提高程序的吞吐量。試想一下一臺(tái)基于同步IO的HTTP服務(wù)器,一種不難想到的實(shí)現(xiàn)方式是每accept一個(gè)連接,就創(chuàng)建一個(gè)新的線程來處理這個(gè)連接的IO,最后當(dāng)這個(gè)連接斷開時(shí)銷毀這個(gè)線程。這么實(shí)現(xiàn)當(dāng)然可以,但創(chuàng)建和銷毀線程的開銷是很大的,而且這要求線程調(diào)度器能夠很好地分配線程之間的時(shí)間。使用IO多路復(fù)用的方式能夠利用有限(甚至單線程)處理許多連接的IO,而不至于浪費(fèi)過多的資源。

關(guān)于協(xié)程和回調(diào)函數(shù)的性能:我想二者應(yīng)當(dāng)是差不多的,或者協(xié)程可能還會(huì)更差一點(diǎn),因?yàn)閽炱饏f(xié)程和恢復(fù)協(xié)程需要執(zhí)行一些額外操作。不過既然性能還沒有緊張到需要去摳dpdk,那么和這一點(diǎn)點(diǎn)的性能優(yōu)勢(shì)相比較的話,代碼的可維護(hù)性和可讀性絕對(duì)也是不容忽視的問題。

關(guān)于異步IO的性能:我們通常講異步IO性能更好指的是吞吐量,而不是低延時(shí)。不論是reactor模式還是proactor模式,其設(shè)計(jì)主旨都是要讓CPU在等待IO的時(shí)候去處理別的事情,不要讓CPU閑下來。如果低延時(shí)很重要的話,應(yīng)當(dāng)考慮使用同步IO與輪詢的方式。

設(shè)計(jì)思路

根據(jù)第一部分,設(shè)計(jì)的基調(diào)就能夠定下來了。我們重新考慮一下需要做的事情:

  1. 當(dāng)我們執(zhí)行到co_await read(...)等異步IO時(shí),掛起當(dāng)前協(xié)程,去處理其他事情;
  2. 當(dāng)異步IO執(zhí)行完畢時(shí),恢復(fù)協(xié)程的執(zhí)行。

仔細(xì)思考一下上述兩點(diǎn),我們就能夠得到所有要做的事情:

  1. 我們需要適時(shí)掛起協(xié)程,所以首先我們要實(shí)現(xiàn)協(xié)程task
  2. 協(xié)程可能會(huì)調(diào)用協(xié)程,所以需要維護(hù)一下協(xié)程的調(diào)用棧(我是在promise里維護(hù)的);
  3. 協(xié)程是用來處理異步IO的,所以我們需要有一些組件來處理io_uring的IO(我是在io_context_worker中處理的);
  4. 當(dāng)異步IO執(zhí)行完畢時(shí),需要有什么東西恢復(fù)協(xié)程的執(zhí)行(這也是在io_context_worker中處理的);
  5. 當(dāng)整個(gè)協(xié)程執(zhí)行完畢時(shí),需要銷毀協(xié)程(這也是在io_context_worker中處理的)。

在繼續(xù)閱讀之前,我先貼一下代碼。對(duì)照著代碼看的話會(huì)舒服一些:GitHub。

task與promise

taskpromise均在coco/task.hpp中定義。我對(duì)task的定位正如協(xié)程最基本的功能——能夠掛起和恢復(fù)的函數(shù)。task類本身只是對(duì)std::coroutine_handle的簡(jiǎn)易封裝。在這里我只介紹一下taskoperator co_await。

taskoperator co_await只是返回task_awaitable,所以co_await處理的重點(diǎn)實(shí)際上是在task_awaitable中實(shí)現(xiàn)的??紤]一下,當(dāng)我們co_await一個(gè)task時(shí),我們究竟是在干什么:

  1. 掛起當(dāng)前協(xié)程
  2. 維護(hù)協(xié)程的調(diào)用棧
  3. 啟動(dòng)被co_await的協(xié)程

task_awaitable::await_suspend()中很容易看出這三點(diǎn):

template <class T>
template <class Promise>
auto task_awaitable<T>::await_suspend(
    std::coroutine_handle<Promise> caller) noexcept -> coroutine_handle {
    // Set caller for this coroutine.
    promise_base &base = static_cast<promise_base &>(m_coroutine.promise());
    promise_base &caller_base = static_cast<promise_base &>(caller.promise());
    base.m_caller_or_top      = &caller_base;

    // Maintain stack bottom and top.
    promise_base *stack_bottom = caller_base.m_stack_bottom;
    assert(stack_bottom == stack_bottom->m_stack_bottom);

    base.m_stack_bottom           = stack_bottom;
    stack_bottom->m_caller_or_top = &base;

    return m_coroutine;
}

這里著重講一下維護(hù)協(xié)程的調(diào)用棧。對(duì)于協(xié)程而言,至少存在一個(gè)協(xié)程,它是在協(xié)程外創(chuàng)建的(比如main函數(shù))。因?yàn)樗辉趨f(xié)程中,所以它也無法被co_await,這個(gè)協(xié)程我們稱之為協(xié)程的棧底。在這個(gè)協(xié)程的執(zhí)行過程中,它可能會(huì)創(chuàng)建和執(zhí)行新的協(xié)程。當(dāng)新的協(xié)程執(zhí)行完畢時(shí),它們會(huì)被清理,并且將執(zhí)行權(quán)交還給調(diào)用者,這與普通函數(shù)的調(diào)用棧是一樣的,只不過這個(gè)功能需要我們自己來實(shí)現(xiàn)。

因?yàn)?code>promise在內(nèi)存中的位置是不可移動(dòng)的(我禁止了promise的拷貝與移動(dòng)),所以我直接采用了類似鏈表的方式將協(xié)程的調(diào)用棧串了起來。在promise_base中,有兩個(gè)成員變量用來維護(hù)這個(gè)調(diào)用棧:

promise_base *m_caller_or_top;
promise_base *m_stack_bottom;

因?yàn)榈谝粋€(gè)變量被復(fù)用了(具備不同的含義),所以可能有點(diǎn)亂。對(duì)于棧底協(xié)程而言,m_caller_or_top指向當(dāng)前調(diào)用棧的棧頂協(xié)程,對(duì)于其他協(xié)程而言,m_caller_or_top指向自己的調(diào)用者(父協(xié)程)。這么設(shè)計(jì)是因?yàn)?strong>棧底協(xié)程不存在調(diào)用者,所以就干脆用這個(gè)變量存一下棧頂了。m_stack_bottom顧名思義,就是指向棧底的協(xié)程。對(duì)于棧底協(xié)程而言,這個(gè)變量指向的就是它自己了。

有了m_caller_or_top,當(dāng)一個(gè)協(xié)程執(zhí)行完畢時(shí),就能方便地找到它的父協(xié)程并交換執(zhí)行權(quán)。有了m_stack_bottomm_caller_or_top,我們就能很方便地找到協(xié)程的棧底和棧頂。當(dāng)需要恢復(fù)task時(shí),就能夠保證總是恢復(fù)棧頂?shù)膮f(xié)程。

當(dāng)協(xié)程執(zhí)行完畢時(shí),需要將控制權(quán)交還給父協(xié)程。我們考慮一下交還控制權(quán)需要做的事情:

  1. 維護(hù)調(diào)用棧,變更棧頂
  2. 如果不是棧底,則恢復(fù)父協(xié)程的執(zhí)行

協(xié)程執(zhí)行完畢時(shí)會(huì)去嘗試執(zhí)行promisefinal_suspend(),因此這部分代碼在promisefinal_suspend()中實(shí)現(xiàn)。final_suspend()返回的類型叫promise_awaitable,其對(duì)應(yīng)的代碼如下:

template <class Promise>
auto promise_awaitable::await_suspend(
    std::coroutine_handle<Promise> coroutine) noexcept
    -> std::coroutine_handle<> {
    promise_base &base  = static_cast<promise_base &>(coroutine.promise());
    promise_base *stack = base.m_stack_bottom;

    // Stack bottom completed. Nothing to resume.
    if (stack == &base)
        return std::noop_coroutine();

    // Set caller coroutine as the top of the stack.
    promise_base *caller   = base.m_caller_or_top;
    stack->m_caller_or_top = caller;

    // Resume caller.
    return caller->m_coroutine;
}

這段代碼應(yīng)該非常易懂,不過我們很容易聯(lián)想到一個(gè)問題:既然交還了控制權(quán),那么它是在何時(shí)銷毀的?

其實(shí)這也不難想到,協(xié)程是由父協(xié)程銷毀的。協(xié)程的返回值存放在promise中,當(dāng)父協(xié)程co_await sometask時(shí),父協(xié)程還需要讀取子協(xié)程的promise以獲取返回值。當(dāng)子協(xié)程task<T>析構(gòu)時(shí),子協(xié)程才真正被銷毀。

io_context的設(shè)計(jì)

既然協(xié)程需要異步地處理IO,那么必然需要個(gè)處理IO的地方,就是io_contextio_context維護(hù)了一個(gè)線程池,線程池中每一個(gè)線程均執(zhí)行一個(gè)worker,每個(gè)worker均維護(hù)一個(gè)io_uring來處理本線程的IO事件和協(xié)程。當(dāng)需要提交新的task給線程池的時(shí)候,由io_context分配給某一個(gè)worker執(zhí)行。

這聽起來和reactor模式好像沒啥區(qū)別,用epoll寫reactor模式的時(shí)候基本上也是這么干的,這是因?yàn)槲冶緛砭褪菑膔eactor模式那邊搬過來的。不過相比于reactor模式,這么做還是有不少細(xì)節(jié)要處理的。在使用io_uring時(shí),每次我們啟動(dòng)異步IO時(shí),都需要獲取到io_uring對(duì)象——要使用io_uring_prep_<io_operation>系列函數(shù),我們必須從io_uring對(duì)象中獲取一個(gè)sqe。而如上所述,io_uring對(duì)象在worker中,這就造成了一個(gè)麻煩:我們無法在io_contexttask分配worker的時(shí)候?qū)?code>io_uring對(duì)象的引用(指針)傳遞給task。雖然使用全局變量不失為一種選擇,但我不想這么做,因?yàn)橐苍S使用者想要在一個(gè)進(jìn)程中創(chuàng)建幾個(gè)不同的io_context用呢。

雖然無法直接將io_uring的引用傳遞給task,但還有一種方法可以進(jìn)行交互。在所有awaitableawait_suspend中,我們可以拿到當(dāng)前協(xié)程的coroutine_handle,而在io_context中,我們也能拿到taskcoroutine_handle,因此可以通過promise來傳遞io_uring的引用。

具體在實(shí)現(xiàn)時(shí),我沒有傳遞io_uring的引用,而是傳遞了worker的指針。這么做是因?yàn)楫?dāng)初我想同時(shí)支持IOCP,傳遞worker可以省掉一些麻煩,雖然后來放棄了。worker的指針只被放在協(xié)程棧的棧底,這么做是因?yàn)楫?dāng)協(xié)程在不同worker之間轉(zhuǎn)移時(shí),能夠很方便地修改協(xié)程所屬的worker(只需要修改棧底就可以了),盡管后來也沒有實(shí)現(xiàn)work-stealing隊(duì)列。在promise中,處理worker的方法如下所示:

/// \brief
///   Set I/O context for current coroutine.
/// \param[in] io_ctx
///   I/O context to be set for current coroutine.
auto set_worker(io_context_worker *io_ctx) noexcept -> void {
    m_stack_bottom->m_worker.store(io_ctx, std::memory_order_release);
}

/// \brief
///   Get I/O context for current coroutine.
/// \return
///   I/O context for current coroutine.
[[nodiscard]] auto worker() const noexcept -> io_context_worker * {
    return m_stack_bottom->m_worker.load(std::memory_order_acquire);
}

不過這么做也有一個(gè)缺點(diǎn),就是io_context侵入了promise的設(shè)計(jì),使得task必須在io_context中才能發(fā)揮作用。

awaitable的設(shè)計(jì)

awaitablecoco/io.hpp中定義。各種awaitable的設(shè)計(jì)就比較簡(jiǎn)單了。以read_awaitable為例,它在await_suspend()中獲取當(dāng)前協(xié)程所屬的worker,然后啟用異步IO,如下所示:

template <class Promise>
auto await_suspend(std::coroutine_handle<Promise> coro) noexcept -> bool {
    m_userdata.coroutine = coro.address();
    return this->suspend(coro.promise().worker());
}

獲取了當(dāng)前協(xié)程的worker后,就轉(zhuǎn)入this->suspend()函數(shù)中去執(zhí)行了。suspend()方法主要的工作是啟動(dòng)異步IO操作,并掛起當(dāng)前協(xié)程:

auto coco::read_awaitable::suspend(io_context_worker *worker) noexcept -> bool {
    assert(worker != nullptr);
    m_userdata.cqe_res   = 0;
    m_userdata.cqe_flags = 0;

    io_uring     *ring = worker->io_ring();
    io_uring_sqe *sqe  = io_uring_get_sqe(ring);
    while (sqe == nullptr) [[unlikely]] {
        io_uring_submit(ring);
        sqe = io_uring_get_sqe(ring);
    }

    io_uring_prep_read(sqe, m_file, m_buffer, m_size, m_offset);
    sqe->user_data = reinterpret_cast<uint64_t>(&m_userdata);

    int result = io_uring_submit(ring);
    if (result < 0) [[unlikely]] { // Result is -errno.
        m_userdata.cqe_res = result;
        return false;
    }

    return true;
}

我沒有把啟用異步IO部分放到模板函數(shù)中,這是因?yàn)閷?duì)于各種不同的IO操作,這部分的代碼實(shí)際上大同小異。但考慮到這部分代碼的長度,放到模板中可能會(huì)導(dǎo)致比較嚴(yán)重的二進(jìn)制膨脹,所以就單獨(dú)拿出來放到.cpp文件中了。

如果去翻我之前的commit記錄的話,會(huì)發(fā)現(xiàn)起初我并沒有把各種awaitable暴露出來,而是讓各種異步操作(比如connection.receive())返回task。后來將awaitable暴露出來是考慮到諸如read、write等操作可能會(huì)被頻繁地調(diào)用,而每次創(chuàng)建一個(gè)協(xié)程都需要申請(qǐng)一次堆內(nèi)存,在循環(huán)中執(zhí)行的話可能對(duì)運(yùn)行效率有比較嚴(yán)重的影響。

關(guān)于代碼

再放一遍代碼地址:GitHub

這份代碼不長,總共兩三千行,而且其中一多半都是注釋,結(jié)合本文的話應(yīng)該不會(huì)很難讀。這本身只是一份實(shí)驗(yàn)性質(zhì)的代碼,同時(shí)我希望它適合拿來學(xué)習(xí),所以我并沒有打算塞入太多的功能。除此之外,我不是很建議你拿來放到工程中使用,因?yàn)槲铱赡軙?huì)一時(shí)興起做出一些breaking change。如果你真的有這個(gè)需要的話,我建議你fork一份代碼自己維護(hù)。

一些可能會(huì)被問到的問題

  1. 為什么沒有實(shí)現(xiàn)UDP相關(guān)的IO?

因?yàn)?code>io_uring似乎還沒有支持recvfrom,至少我實(shí)現(xiàn)的時(shí)候還沒有。

  1. 為什么不使用mmap和內(nèi)核共享內(nèi)存/為什么不向io_uring注冊(cè)文件描述符等性能相關(guān)的問題

因?yàn)槲也皇?code>io_uring專家。我寫這個(gè)庫的目的是學(xué)習(xí)用C++20的協(xié)程架構(gòu)一個(gè)異步IO庫,做這些性能優(yōu)化會(huì)加大架構(gòu)難度,并且花掉我大量的頭發(fā)和時(shí)間,使我本不茂密的頭發(fā)雪上加霜。除此之外,你會(huì)發(fā)現(xiàn)我也沒有實(shí)現(xiàn)work-stealing隊(duì)列,原因同理。Round Robin的性能雖然不至于最優(yōu),但也不會(huì)太差。

  1. 考不考慮加上HTTP支持?

考慮過,太懶所以放棄了。一方面是手寫HTTP parser還是挺麻煩的,就算能用bison自動(dòng)生成也還得去啃RFC。另一方面是,TCP作為一種基于流的協(xié)議,我沒有想好如何處理連接的緩存能夠兼顧性能和使用的便捷性。如果你有這方面的需求的話,不妨先用著其他的HTTP parser,比如llhttp。

  1. 為什么沒有實(shí)現(xiàn)yield?

我覺得異步IO一般用不到這東西,所以就沒寫。如果需要的話就自己實(shí)現(xiàn)吧。

  1. 會(huì)支持Windows(IOCP)嗎?

我有考慮過支持IOCP,但I(xiàn)OCP不支持定時(shí)器,我又不想刪除Linux這邊的timer,所以暫且沒有這個(gè)想法。

  1. 作者你README寫得好水啊

確實(shí),我也覺得好水啊,有沒有好心人幫忙修一修啊。

  1. 為什么不用中文寫注釋和README?

不用中文寫注釋是因?yàn)閏lang-format沒法處理中文的斷行。不用中文寫README是因?yàn)閼校幌雽憙煞軷EADME。說不定哪天心情好了就寫一份中文版。

碎碎念

要不要塞張插圖呢?文章來源地址http://www.zghlxwxcb.cn/news/detail-843742.html

到了這里,關(guān)于使用C++20協(xié)程和io_uring優(yōu)雅地實(shí)現(xiàn)異步IO的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Linux的進(jìn)程,協(xié)程和線程

    Linux的進(jìn)程、協(xié)程和線程是計(jì)算機(jī)科學(xué)中重要的概念,它們?cè)诓僮飨到y(tǒng)和并發(fā)編程中發(fā)揮著關(guān)鍵的作用。讓我們逐個(gè)詳解這些概念,并討論它們之間的關(guān)系。 進(jìn)程是操作系統(tǒng)中的一個(gè)執(zhí)行單元,它包含了程序執(zhí)行所需的所有資源,如內(nèi)存空間、文件描述符、寄存器等。 進(jìn)程是

    2024年01月23日
    瀏覽(20)
  • 多線程、協(xié)程和多進(jìn)程并發(fā)編程

    37.1 如何通俗理解線程和進(jìn)程? 進(jìn)程:進(jìn)程就是正在執(zhí)?的程序。 線程:是程序執(zhí)?的?條路徑, ?個(gè)進(jìn)程中可以包含多條線程。 通俗理解:例如你打開抖?,就是打開?個(gè)進(jìn)程,在抖???和朋友聊天就是開啟了?條線程。 再舉?個(gè)例?: 在某?堂打飯的時(shí)候,此?堂安

    2024年02月02日
    瀏覽(32)
  • unity 等待事件之協(xié)程和Invoke

    提示:文章寫完后,目錄可以自動(dòng)生成,如何生成可參考右邊的幫助文檔 提示:這里可以添加本文要記錄的大概內(nèi)容: 協(xié)程的用法 和 Invoke 的等待事件使用 提示:以下是本篇文章正文內(nèi)容,下面案例可供參考 代碼如下(示例): 好記性不如爛筆頭!

    2024年04月13日
    瀏覽(28)
  • 【Unity每日一記】“調(diào)皮的協(xié)程”,協(xié)程和多線程的區(qū)別在哪里

    【Unity每日一記】“調(diào)皮的協(xié)程”,協(xié)程和多線程的區(qū)別在哪里

    ?????個(gè)人主頁 :@元宇宙-秩沅 ????? hallo 歡迎 點(diǎn)贊?? 收藏? 留言?? 加關(guān)注?! ????? 本文由 秩沅 原創(chuàng) ????? 收錄于專欄 : unity每日一記 ?【軟件設(shè)計(jì)師高頻考點(diǎn)暴擊】 ?【Unityc#專題篇】之c#系統(tǒng)化大禮包】 ?【unity數(shù)據(jù)持久化】數(shù)據(jù)管理類_PlayerPrfs ?【u

    2024年02月05日
    瀏覽(56)
  • 淺談Lua協(xié)程和函數(shù)的尾調(diào)用

    淺談Lua協(xié)程和函數(shù)的尾調(diào)用

    雖然不經(jīng)常用到協(xié)程,但是也不能談虎色變。同時(shí),在有些場(chǎng)景,協(xié)程會(huì)起到一種不可比擬的作用。所以,了解它,對(duì)于一些功能,也會(huì)有獨(dú)特的思路和想法。 概念 關(guān)于進(jìn)程和線程的概念就不多說。 那么從多線程的角度來看,協(xié)程和線程有點(diǎn)類似:擁有自己的棧,局部變量

    2024年02月10日
    瀏覽(16)
  • unity協(xié)程 Start/StopCoroutine() 結(jié)束協(xié)程和再次啟動(dòng)協(xié)程存在的問題和解決方案

    unity協(xié)程 Start/StopCoroutine() 結(jié)束協(xié)程和再次啟動(dòng)協(xié)程存在的問題和解決方案

    僅用于記錄遇到的問題和解決方案。 快速閱覽: 一、結(jié)束協(xié)程無效: 協(xié)程方法需要單獨(dú)存一份private IEnumerator myTest,再開始和結(jié)束不直接傳入方法名,而是使用這份保存的myTest進(jìn)行開始和結(jié)束。 二、再次開啟協(xié)程時(shí)少跑了幾行代碼: 再次開始同一個(gè)方法名的協(xié)程時(shí),不是從

    2024年02月15日
    瀏覽(25)
  • SpringBoot如何優(yōu)雅的實(shí)現(xiàn)異步調(diào)用?

    Spring Boot 提供了多種方式來實(shí)現(xiàn)異步任務(wù),這里介紹三種主要實(shí)現(xiàn)方式。 Spring Boot 提供了多種方式來實(shí)現(xiàn)異步任務(wù),這里介紹三種實(shí)現(xiàn)方式。 @Async 注解是 Spring 提供的一種輕量級(jí)異步方法實(shí)現(xiàn)方式,它可以標(biāo)記在方法上,用來告訴 Spring 這個(gè)方法是一個(gè)異步方法,Spring 會(huì)將

    2024年02月07日
    瀏覽(15)
  • 【SpringBoot系列】如何優(yōu)雅地實(shí)現(xiàn)異步調(diào)用

    【SpringBoot系列】如何優(yōu)雅地實(shí)現(xiàn)異步調(diào)用

    1.前言 在現(xiàn)代的應(yīng)用程序開發(fā)中,異步調(diào)用是提高系統(tǒng)性能和響應(yīng)能力的重要手段之一。 Spring Boot作為一個(gè)快速開發(fā)框架,提供了多種方式來實(shí)現(xiàn)異步調(diào)用,使得開發(fā)者能夠更加優(yōu)雅地處理并發(fā)和異步任務(wù)。 本文將介紹如何在Spring Boot中實(shí)現(xiàn)異步調(diào)用的方法和技巧,幫助開發(fā)

    2024年02月07日
    瀏覽(24)
  • 如何在不依賴于Mq的形式下更優(yōu)雅地實(shí)現(xiàn)異步任務(wù)處理?。。? decoding=

    如何在不依賴于Mq的形式下更優(yōu)雅地實(shí)現(xiàn)異步任務(wù)處理?。?!

    考慮到現(xiàn)有業(yè)務(wù)很多依賴于 MQ的方式進(jìn)行,這種方式需要依賴于MQ,發(fā)送消息到mq和消費(fèi)mq消息時(shí)需要了解mq消息結(jié)構(gòu)進(jìn)行相應(yīng)處理; 對(duì)于后續(xù)對(duì)同樣的事件做其他處理的人如不能提前了解到已有相應(yīng)消息發(fā)到了mq就得再發(fā)一次消息到mq等。 圖1.1 消息中間件 現(xiàn)需要考慮在不依賴

    2024年02月19日
    瀏覽(19)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包