• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            Life is Good.

            Enhance Tech and English
            隨筆 - 65, 文章 - 20, 評論 - 21, 引用 - 0
            數據加載中……

            【轉】C++多線程入門(一)

            第1節   背景
                為了更好的理解多線程的概念,先對進程,線程的概念背景做一下簡單介紹。早期的計算機系統都只允許一個程序獨占系統資源,一次只能執行一個程序。在大型機 年代,計算能力是一種寶貴資源。對于資源擁有方來說,最好的生財之道自然是將同一資源同時租售給盡可能多的用戶。最理想的情況是壟斷全球計算市場。所以不 難理
            解為何當年IBM預測“全球只要有4臺計算機就夠了”。
                這種背景下,一個計算機能夠支持多個程序并發執行的需求變得十分迫切。由此產生了進程的概念。進程在多數早期多任務操作
            系統中是執行工作的基本單元。進程是包含程序指令和相關資源的集合。每個進程和其他進程一起參與調度,競爭CPU,內存等系統資源。每次進程切換,都存在進程資源的保存和恢復動作,這稱為上下文切換。
               進程的引入可以解決支持多用戶的問題,但是多進程系統也在如下方面產生了新的問題:進程頻繁切換引起的額外開銷可能會嚴重影響系統性能。
            進程間通信要求復雜的系統級實現。
                 在程序功能日趨復雜的情況下,上述缺陷也就凸現出來。比如,一個簡單的GUI程序,為了有更好的交互性,通常用一個任務支持界面交互,另一個任務支持后臺 運算。如果每個任務均由一個進程來實現,那會相當低效。對每個進程來說,系統資源看上去都是其獨占的。比如內存空間,每個進程認為自己的內存空間是獨有 的。一次切換,這些獨立資源都需要切換。
               由此就演化出了利用分配給同一個進程的資源,盡量實現多個任務的方法。這也就引入了線程的概念。同一個進程內部的多個線程,共享的是同一個進程的所有資 源。比如,與每個進程獨有自己的內存空間不同,同屬一個進程的多個線程共享該進程的內存空間。例如在進程地址空間中有一個全局變量globalVar,若 A線程將其賦值為1,則另一線程B可以看到該變量值為1。兩個線程看到的全局變量globalVar是同一個變量。通過線程可以支持同一個應用程序內部的 并發,免去了進程頻繁切換的開銷,另外并發任務間通信也更簡單。
                目前多線程應用主要用于兩大領域:網絡應用和嵌入式應用。為什么在這兩個領域應用較多呢?因為多線程應用能夠解決兩大問題:并發。網絡程序具有天生的并發 性。比如網絡數據庫可能需要同時處理數以千計的請求。而由于網絡連接的時延不確定性和不可靠性,一旦等待一次網絡交互,可以讓當前線程進入睡眠,退出調 度,處理其他線程。這樣就能夠有效利用系統資源,充分發揮系統  實時處理能力。線程的切換是輕量級的,所以可以保證足夠快。每當有事件發生,狀態改變,都能有線程及時響應,而且每次線程內部處理的計算強度和復雜度都不 大。在這種情況下,多線程實現的模型也是高效的。在有些語言中,對多線程或者并發的支持是直接內建在語言中的,比如Ada和VHDL。在C++里面,對多 線程的支持由具體操作系統提供的函數接口支持。不同的系統中具體實現方法不同。后面所有例子只給出windows和Unix/Linux的實現。在后面的 實現中,考慮的是盡量封裝隔離底層的多線程函數接口,屏蔽操作系統底層的線程實現具體細節,介紹的重點是多線程編程中較通用的概念。同時也盡量體現C++ 面向對象的一面。最后,由于空閑時間有限,我只求示例代碼能夠明確表達自己的意思即可。至于代碼的盡善盡美就只能有勞各位盡力以為之了。

            第2節   線程的創建
            本節介紹如下內容:線程狀態、線程運行環境、線程類定義
            示例程序:線程類的Windows和Unix實現
            線程狀態
                在一個線程的生存期內,可以在多種狀態之間轉換。不同操作系統可以實現不同的線程模型,定義許多不同的線程狀態,每個狀態還可以包含多個子狀態。但大體說來,如下幾種狀態是通用的:
                   就緒:參與調度,等待被執行。一旦被調度選中,立即開始執行。
                   運行:占用CPU,正在運行中。
                   休眠:暫不參與調度,等待特定事件發生。
                   中止:已經運行完畢,等待回收線程資源(要注意,這個很容易誤解,后面解釋)。
            線程環境
            線程存在于進程之中。進程內所有全局資源對于內部每個線程均是可見的。
            進程內典型全局資源有如下幾種:
                代碼區。這意味著當前進程空間內所有可見的函數代碼,對于每個線程來說也是可見的。
                   靜態存儲區。全局變量。靜態變量。  動態存儲區。也就是堆空間。
            線程內典型的局部資源有:
                   本地棧空間。存放本線程的函數調用棧,函數內部的局部變量等。
                   部分寄存器變量。例如本線程下一步要執行代碼的指針偏移量。
            一個進程發起之后,會首先生成一個缺省的線程,通常稱這個線程為主線程。C/C++程序中主線程就是通過main函數進入的線程。由主線程衍生的線程稱為從線程,從線程也可以有自己的入口函數,作用相當于主線程的main函數。
                  這個函數由用戶指定。Pthread和winapi中都是通過傳入函數指針實現。在指定線程入口函數時,也可以指定入口函數的參數。就像main函數有固 定的格式要求一樣,線程的入口函數一般也有固定的格式要求,參數通常都是void *類型,返回類型在pthread中是void *, winapi中是unsigned int,而且都需要是全局函數。最常見的線程模型中,除主線程較為特殊之外,其他線程一旦被創建,相互之間就是對等關系 (peer to peer), 不存在隱含的層次關系。每個進程可以創建的最大線程數由具體實現決定。
            為了更好的理解上述概念,下面通過具體代碼來詳細說明。
            線程類接口定義。一個線程類無論具體執行什么任務,其基本的共性無非就是: 創建并啟動線程、停止線程。另外還有就是能睡,能等,能分離執行(有點拗口,后面再解釋)。
                   還有其他的可以繼續加…
            將線程的概念加以抽象,可以為其定義如下的類:
            文件 thread.h
            #ifndef __THREAD__H_
            #define __THREAD__H_
            class Thread
            {
            public:
            Thread();
            virtual ~Thread();
            int start (void * = NULL);
            void stop();
            void sleep (int);
            void detach();
            void * wait();
            protected:
            virtual void * run(void *) = 0;
            private:
            //這部分win和unix略有不同,先不定義,后面再分別實現。
            //順便提一下,我很不習慣寫中文注釋,這里為了更明白一
            //點還是選用中文。

            };
            #endif
            Thread::start()函數是線程啟動函數,其輸入參數是無類型指針。
            Thread::stop()函數中止當前線程。
            Thread::sleep()函數讓當前線程休眠給定時間,單位為秒。
            Thread::run()函數是用于實現線程類的線程函數調用。
            Thread::detach()和thread::wait()函數涉及的概念略復雜一些。在稍后再做解釋。
            Thread類是一個虛基類,派生類可以重載自己的線程函數。下面是一個例子。
            示例程序
            代碼寫的都不夠精致,暴力類型轉換比較多,歡迎有閑階級美化,謝過了先。
            文件create.h
            #ifndef __CREATOR__H_
            #define __CREATOR__H_
            #include <stdio.h>
            #include "thread.h"
            class Create: public Thread
            {
            protected:
            void * run(void * param)
            {
                char * msg = (char*) param;
                printf ("%s\n", msg);
                //sleep(100); 可以試著取消這行注釋,看看結果有什么不同。
                printf("One day past.\n");
                return NULL;
            }
            };
            #endif
            然后,實現一個main函數,來看看具體效果:
            文件Genesis.cpp
            #include <stdio.h>
            #include "create.h"
            int main(int argc, char** argv)
            {
            Create monday;
            Create tuesday;
            printf("At the first God made the heaven and the earth.\n");
            monday.start("Naming the light, Day, and the dark, Night, the first day.");
            tuesday.start("Gave the arch the name of Heaven, the second day.");
            printf("These are the generations of the heaven and the earth.\n");
            return 0;
            }
            編譯運行,程序輸出如下:
            At the first God made the heaven and the earth.
            These are the generations of the heaven and the earth.
            令人驚奇的是,由周一和周二對象創建的子線程似乎并沒有執行!這是為什么呢?別急,在最后的printf語句之前加上如下語句:
            monday.wait();
            tuesday.wait();
            重新編譯運行,新的輸出如下:
            At the first God made the heaven and the earth.
            Naming the light, Day, and the dark, Night, the first day.
            One day past.
            Gave the arch the name of Heaven, the second day.
            One day past.
            These are the generations of the heaven and the earth.
            為了說明這個問題,需要了解前面沒有解釋的Thread::detach()和Thread::wait()兩個函數的含義。
                   無論在windows中,還是Posix中,主線程和子線程的默認關系是:無論子線程執行完畢與否,一旦主線程執行完畢退出,所有子線程執行都會終止。這 時整個進程結束或僵死(部分線程保持一種終止執行但還未銷毀的狀態,而進程必須在其所有線程銷毀后銷毀,這時進程處于僵死狀態),在第一個例子的輸出中, 可以看到子線程還來不及執行完畢,主線程的main()函數就已經執行完畢,從而所有子線程終止。
                 需要強調的是,線程函數執行完畢退出,或以其他非常方式終止,線程進入終止態(請回顧上面說的線程狀態),但千萬要記住的是,進入終止態后,為線程分配的 系統資源并不一定已經釋放,而且可能在系統重啟之前,一直都不能釋放。終止態的線程,仍舊作為一個線程實體存在與操作系統中。(這點在win和unix中 是一致的。)而什么時候銷毀線程,取決于線程屬性。通常,這種終止方式并非我們所期望的結果,而且一個潛在的問題是未執行完就終止的子線程,除了作為線程 實體占用系統資源之外,其線程函數所擁有的資源(申請的動態內存,打開的文件,打開的網絡端口等)也不一定能釋放。所以,針對這個問題,主線程和子線程之 間通常定義兩種關系:
                  可會合(joinable)。這種關系下,主線程需要明確執行等待操作。在子線程結束后,主線程的等待操作執行完畢,子線程和主線程會合。這時主線程繼續 執行等待操作之后的下一步操作。主線程必須會合可會合的子線程,Thread類中,這個操作通過在主線程的線程函數內部調用子線程對象的wait()函數 實現。這也就是上面加上三個wait()調用后顯示正確的原因。必須強調的是,即使子線程能夠在主線程之前執行完畢,進入終止態,也必需顯示執行會合操 作,否則,系統永遠不會主動銷毀線程,分配給該線程的系統資源(線程id或句柄,線程管理相關的系統資源)也永遠不會釋放。
                  相分離(detached)。顧名思義,這表示子線程無需和主線程會合,也就是相分離的。這種情況下,子線程一旦進入終止態,系統立即銷毀線程,回收資 源。無需在主線程內調用wait()實現會合。Thread類中,調用detach()使線程進入detached狀態。
            這種方式常用在線程數較多的情況,有時讓主線程逐個等待子線程結束,或者讓主線程安排每個子線程結束的等待順序,是很困
            難或者不可能的。所以在并發子線程較多的情況下,這種方式也會經常使用。
            缺省情況下,創建的線程都是可會合的。可會合的線程可以通過調用detach()方法變成相分離的線程。但反向則不行。

            UNIX實現
            文件 thread.h
            #ifndef __THREAD__H_
            #define __THREAD__H_
            class Thread
            {
            public:
            Thread();
            virtual ~Thread();
            int start (void * = NULL);
            void stop();
            void sleep (int);
            void detach();
            void * wait();
            protected:
            virtual void * run(void *) = 0;
            private:
            pthread_t handle;
            bool started;
            bool detached;
            void * threadFuncParam;
            friend void * threadFunc(void *);
            };

            //pthread中線程函數必須是一個全局函數,為了解決這個問題
            //將其聲明為靜態,以防止此文件之外的代碼直接調用這個函數。
            //此處實現采用了稱為Virtual friend function idiom 的方法。
            Static void * threadFunc(void *);
            #endif
            文件thread.cpp
            #include <pthread.h>
            #include <sys/time.h>
            #include “thread.h”

            static void * threadFunc (void * threadObject)
            {
            Thread * thread = (Thread *) threadObject;
            return thread->run(thread->threadFuncParam);
            }

            Thread::Thread()
            {
            started = detached = false;
            }

            Thread::~Thread()
            {
            stop();
            }

            bool Thread::start(void * param)
            {
            pthread_attr_t attributes;
            pthread_attr_init(&attributes);
            if (detached)
            {
                pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_DETACHED);
            }

            threadFuncParam = param;

            if (pthread_create(&handle, &attributes, threadFunc, this) == 0)
            {
                started = true;
            }

            pthread_attr_destroy(&attribute);
            }
            void Thread::detach()
            {
            if (started && !detached)
            {
                pthread_detach(handle);
            }
            detached = true;
            }

            void * Thread::wait()
            {
            void * status = NULL;
            if (started && !detached)
            {
                pthread_join(handle, &status);
            }
            return status;
            }

            void Thread::stop()
            {
            if (started && !detached)
            {
                pthread_cancel(handle);
                pthread_detach(handle);
                detached = true;
            }
            }

            void Thread::sleep(unsigned int milliSeconds)
            {
            timeval timeout = { milliSeconds/1000, millisecond%1000};
            select(0, NULL, NULL, NULL, &timeout);
            }

            Windows實現
            文件thread.h
            #ifndef _THREAD_SPECIFICAL_H__
            #define _THREAD_SPECIFICAL_H__
            #include <windows.h>
            static unsigned int __stdcall threadFunction(void *);
            class Thread {
                    friend unsigned int __stdcall threadFunction(void *);
            public:
                    Thread();
                    virtual ~Thread();
                    int start(void * = NULL);
                    void * wait();
                    void stop();
                    void detach();
                    static void sleep(unsigned int);

            protected:
                    virtual void * run(void *) = 0;

            private:
                    HANDLE threadHandle;
                    bool started;
                    bool detached;
                    void * param;
                    unsigned int threadID;
            };

            #endif

            文件thread.cpp
            #include "stdafx.h"
            #include <process.h>
            #include "thread.h"
            unsigned int __stdcall threadFunction(void * object)
            {
                    Thread * thread = (Thread *) object;
                    return (unsigned int ) thread->run(thread->param);
            }

            Thread::Thread()
            {
                    started = false;
                    detached = false;
            }

            Thread::~Thread()
            {
                    stop();
            }

            int Thread::start(void* pra)
            {
                    if (!started)
                    {
                            param = pra;
                            if (threadHandle = (HANDLE)_beginthreadex(NULL, 0, threadFunction, this, 0, &threadID))
                            {
                                    if (detached)
                                    {
                                            CloseHandle(threadHandle);
                                    }
                                    started = true;
                            }
                    }
                    return started;
            }

            //wait for current thread to end.
            void * Thread::wait()
            {
                    DWORD status = (DWORD) NULL;
                    if (started && !detached)
                    {
                            WaitForSingleObject(threadHandle, INFINITE);
                            GetExitCodeThread(threadHandle, &status);      
                            CloseHandle(threadHandle);
                            detached = true;
                    }

                    return (void *)status;
            }

            void Thread::detach()
            {
            if (started && !detached)
            {
                CloseHandle(threadHandle);
            }
            detached = true;
            }

            void Thread::stop()
            {
                    if (started && !detached)
                    {
                            TerminateThread(threadHandle, 0);

                            //Closing a thread handle does not terminate
                            //the associated thread.
                     //To remove a thread object, you must terminate the thread,
                            //then close all handles to the thread.
                            //The thread object remains in the system until
                      //the thread has terminated and all handles to it have been
                            //closed through a call to CloseHandle
                            CloseHandle(threadHandle);
                            detached = true;
                    }
            }
            void Thread::sleep(unsigned int delay)
            {
                    ::Sleep(delay);
            }

            小結
            本節的主要目的是幫助入門者建立基本的線程概念,以此為基礎,抽象出一個最小接口的通用線程類。在示例程序部分,初學者可以體會到并行和串行程序 執行的差異。有興趣的話,大家可以在現有線程類的基礎上,做進一步的擴展和嘗試。如果覺得對線程的概念需要進一步細化,大家可以進一步擴展和完善現有 Thread類。想更進一步了解的話,一個建議是,可以去看看其他語言,其他平臺的線程庫中,線程類抽象了哪些概念。比如Java, perl等跨平臺語言中是如何定義的,微軟winapi到dotnet中是如何支持多線程的,其線程類是如何定義的。這樣有助于更好的理解線程的模型和基 礎概念。
                 另外,也鼓勵大家多動手寫寫代碼,在此基礎上嘗試寫一些代碼,也會有助于更好的理解多線程程序的特點。比如,先開始的線程不一定先結束。線程的執行可能會交替進行。把printf替換為cout可能會有新的發現,等等。
                每個子線程一旦被創建,就被賦予了自己的生命。管理不好的話,一只特例獨行的豬是非常讓人頭痛的。對于初學者而言,編寫多線程程序可能會遇到很多令人手足 無措的bug。往往還沒到考慮效率,避免死鎖等階段就問題百出,而且很難理解和調試。這是非常正常的,請不要氣餒,后續文章會盡量解釋各種常見問題的原 因,引導大家避免常見錯誤。目前能想到入門階段常遇到的問題是:內存泄漏,系統資源泄漏。
                   程序執行結果混亂,但是在某些點插入sleep語句后結果又正確了。
                   程序crash, 但移除或添加部分無關語句后,整個程序正常運行(假相)。
                   多線程程序執行結果完全不合邏輯,出于預期。
            本文至此,如果自己動手改改,試一些例子,對多線程程序應該多少有一些感性認識了。剛開始只要把基本概念弄懂了,后面可以一步一步搭建出很復雜的 類。不過剛開始不要貪多,否則會欲速則不達,越弄越糊涂。最后,大家見仁見智吧,我在此起到拋磚引玉的作用就很開心了,呵呵。另外文本編輯器的原因,代碼 如果編譯不過,可能需要
            把標點符號從中文換成英文。

            文章出處:http://www.diybl.com/course/3_program/c++/cppsl/20081010/149882.html


            PS:如果遇到編譯錯誤:error C2065: '_beginthreadex' : undeclared identifier.
            做以下設置:
            project->setting->C/C++->Code Generation->Use run-time libray->選 Debug Multithread(多線程),或 Multithread.

            posted on 2011-02-22 14:59 Mike Song 閱讀(662) 評論(0)  編輯 收藏 引用

            91久久精品国产成人久久| 欧洲国产伦久久久久久久| 国产精品99久久精品| 婷婷久久综合九色综合98| 日日狠狠久久偷偷色综合免费| 波多野结衣久久精品| 99精品久久精品一区二区| 久久99精品久久久久久秒播| 久久久久国产| 国产精品久久久久无码av| 久久久久久久久66精品片| 国产欧美久久久精品| 久久综合亚洲鲁鲁五月天| 亚洲国产精品热久久| 久久久久亚洲av无码专区导航 | 国产成人精品白浆久久69| 99热都是精品久久久久久| 少妇高潮惨叫久久久久久| 久久影院亚洲一区| 亚洲国产精品久久久久婷婷软件 | 久久久综合香蕉尹人综合网| 久久人爽人人爽人人片AV| 久久精品国产2020| 女同久久| 色老头网站久久网| 欧美精品一区二区久久| 精品无码人妻久久久久久| 久久er热视频在这里精品| 亚洲精品乱码久久久久久自慰 | 亚洲国产成人久久综合碰碰动漫3d| 一本色道久久综合狠狠躁| 少妇熟女久久综合网色欲| 少妇久久久久久被弄到高潮| 久久青青草原精品国产软件| 久久久久久毛片免费看| 精品国产91久久久久久久| 99久久人妻无码精品系列| 99久久精品午夜一区二区| 久久综合综合久久97色| 国产高潮久久免费观看| 久久精品中文字幕第23页|