伊莉討論區

標題: 將_beginthreadex包成class後,如何傳參數進間接的事件函式 [打印本頁]

作者: 龍謙    時間: 2018-7-23 10:31 PM     標題: 將_beginthreadex包成class後,如何傳參數進間接的事件函式

本帖最後由 龍謙 於 2018-7-23 10:32 PM 編輯

●【題目名稱】:
將_beginthreadex包成class後,如何傳參數進間接的事件函式

●【目前問題】:
小弟想要試著開不同的執行緒並將不同的參數傳入相對應的執行緒內做運算,且為了不讓任何一個執行緒空閒下來,在每次分配執行緒前,都會先看目前誰是空閒的。目前遇到最大的問題,不知道如何將參數傳入不同的執行緒內,若不是包成class時,_beginthreadex的第四個參數是要給不同的執行緒的參數傳入,但現在包成class,需要將事件函式宣告成static,並傳入this指標呼叫自己想呼叫的函式。下面程式碼中,FunctionCalledByThread就是class中間接呼叫原事件的函式;Test就是原本的事件函式,但在class中必需被FunctionCalledByThread間接呼叫;ThreadBlockParam是我要傳給各個執行緒的參數結構。左思右想後,我想到可以將要傳給不同執行緒的參數宣告成class的屬性並直接讓函式讀取,但現在問題來了,不同的函式要如何知道自己是第幾個執行緒,並讀取相對應的參數呢?我原本想要用GetCurrentThreadId取得現在在哪個執行緒中,但後來發現,GetCurrentThreadId取得的是執行緒的編號,而非「第幾個執行緒」。
後來我又想到,再宣告一個的變數(在下面的程式碼中為nowThread),這個變數在每次分配執行緒前,就記錄現在的執行緒,然後在取參數時,就直接到參數陣列的相對位置取值,但這個方法也失敗了,執行結果如下
[attach]124279349[/attach]
感覺都在同一個執行緒,沒有分配到的感覺,我期望的結果應該如下:
In 0-th thread, thread id = 17160!
In 1-th thread, thread id = 1752!
In 2-th thread, thread id = 9856!
In 3-th thread, thread id = 7696!
In 4-th thread, thread id = 17668!
In 5-th thread, thread id = 17796!
In 6-th thread, thread id = 17916!
In 7-th thread, thread id = 16572!
In 0-th thread, thread id = 7588!
In 1-th thread, thread id = 17264!
In 2-th thread, thread id = 16296!
In 3-th thread, thread id = 16244!
In 4-th thread, thread id = 2820!
In 5-th thread, thread id = 16984!
In 6-th thread, thread id = 17948!
In 7-th thread, thread id = 15628!

請問有大大遇過類似的問題並解決的嗎?或是有其他大大有什麼建議或是更好的做法呢?

另外,有大大可以告訴我,為何包成class時,必需將事件函數宣告成static 嗎?我在https://stackoverflow.com/questi ... th-member-functions 這篇文章中只知道要傳入instance的物件而已,但不知道原因。

●【編譯環境】:
作業系統:win 10
IDE: visual studio 2015

●【目前程式碼】:由於程式碼一直貼不上來,所以改貼在codepad中:
http://codepad.org/B3Yid7rc



作者: MY0613    時間: 2018-7-26 04:26 PM

本帖最後由 MY0613 於 2018-7-26 04:27 PM 編輯

[attach]124317827[/attach]

結果:
  1. In 0-th thread, thread id = 4240!
  2. In 1-th thread, thread id = 4496!
  3. In 2-th thread, thread id = 4396!
  4. In 3-th thread, thread id = 4932!
  5. In 4-th thread, thread id = 4704!
  6. In 5-th thread, thread id = 2252!
  7. In 6-th thread, thread id = 4180!
  8. In 7-th thread, thread id = 2220!
  9. In 8-th thread, thread id = 4368!
  10. In 0-th thread, thread id = 2576!
  11. In 1-th thread, thread id = 5068!
  12. In 2-th thread, thread id = 3876!
  13. In 3-th thread, thread id = 3740!
  14. In 4-th thread, thread id = 4624!
  15. In 5-th thread, thread id = 4204!
  16. In 6-th thread, thread id = 4700!
  17. Multiple thread wrapper to class testing is finishing!

  18. Process returned -1073741819 (0xC0000005)   execution time : 18.432 s
  19. Press any key to continue.
複製代碼



需要說明再問吧!


作者: 龍謙    時間: 2018-7-27 11:14 PM

M大太厲害了~~~~
太感謝大大

不過我有些地方不太懂吔,
我有點不太懂ThreadBlockParam 結構中多宣告一個void* _wrap的義意,雖然之道是與this指標有關,但不太清楚它們之間的連結是什麼:
到TestMultThreadSendParam()函式中的_beginthreadex(NULL, 0, FunctionCalledByThread, (tb + nowThread), 0, &ThreadId_For_Beginthreadex_Function);部分我都OK,
但接下來在FunctionCalledByThread(void *ptr_this)函式中,為什麼可以先轉成ThreadBlockParam 的指標;再將ThreadBlockParam 中的void* _wrap轉成this指標呢?
我知道無號指標可以轉成任何型態的指標,但所在的記憶體位置不同,就算使用讀取該類別的指標讀取記憶體裡面的值,這樣不會有問題嗎?


補充內容 (2018-7-28 12:39 AM):
抱歉,看太快了沒看到,M大有做(tb + nowThread)->_wrap = (void*) this;
將自己的指標丟給_wrap
但我試過沒有(tb + nowThread)->_wrap = (void*) this;
好像也可以,為什麼?
作者: MY0613    時間: 2018-7-30 11:20 AM

補充內容 (2018-7-28 12:39 AM):
抱歉,看太快了沒看到,M大有做(tb + nowThread)->_wrap = (void*) this;
將自己的指標丟給_wrap
但我試過沒有(tb + nowThread)->_wrap = (void*) this;
好像也可以,為什麼?

其實我不時懂你這邊的意思,但在 static FunctionCalledByThread function 裡面,
我目的是想用 ThreadBlockParam._wrap 來找回啟動 Thread 的實體 (instance)
(坦白說我也不知道你這邊用 static function 的用意,如果不用 static function 或許 還能更漂亮些)
找回原本的實體 (instance)後,才能操作物件裡的內容(用意上大致如此)

  1.         static unsigned int __stdcall FunctionCalledByThread(void *ptr_this)
  2.         {
  3.                 //TestWrapperMultThreadToClass *_this = static_cast<TestWrapperMultThreadToClass*>(ptr_this);
  4.                 //_this->Test();
  5.                 ThreadBlockParam *_tb = (ThreadBlockParam*)(ptr_this);
  6.                 TestWrapperMultThreadToClass *_this = (TestWrapperMultThreadToClass*)(_tb->_wrap);

  7.         _this->Test(_tb);
  8.                 return 1;
  9.         }
複製代碼


作者: cockroachrun    時間: 2018-7-30 04:01 PM

本帖最後由 cockroachrun 於 2018-7-30 04:46 PM 編輯
MY0613 發表於 2018-7-26 04:26 PM
結果:

這程式有bug
bug 發生在這段code
  1.         while ((tb + ThreadIndex)->IsIdle == false) // 看現在哪個執行緒是空的
  2.         {
  3.                 if (ThreadIndex < _ThreadNum )
  4.                         ThreadIndex++;
  5.                 else
  6.                         ThreadIndex = 0;
  7.                 Sleep(100);
  8.         }
複製代碼
這段code 會讓你的ThreadIndex 為 8 . 由你的結果也發現確實會出現8
當是8 時 (tb+ThreadIndex)->ThreadIndex = ... 就會破壞記億體. 讓程式當掉

以上說的不是重點.
板大的問題. 為什麼傳給thread 的function . 如是class 的function. 必需是static 的.
因為_beginthreadex() 給的thread entry function 必須是傳統的c function.
class 的member function(非static function). 並不是.
I.
class 的非 static member function .雖然你寫這 void F(int) 只有一個int 參數. 但是compiler 會自動幫你加入一個參數. 這個參數你可以想成就是 this .
所以 你的一個 class instance a 去叫用 F(100) 寫成 a.F(100) compiler 回傳給F() 兩個東西. 一個是100, 另一個是 &a . 您可以在F() 當中 用this 去取得.
II.
對於class 的static member function . compiler 不會傳送額外的參數. 叫用方法就跟傳統c 一樣. static member function 當中. 也沒有this 可以使用.

由上訴的I , II 點. 就可以明白. 為什麼要用static member function . 當作是_beginthreadex() 的entry function

板大的另一個問題. 是針對M 大的回應程式當中 :
  1. 但我試過沒有(tb + nowThread)->_wrap = (void*) this;好像也可以,為什麼?
複製代碼
當你把這行去掉為什麼也能跑. 也能得到正確的資料.
很簡單 因為  void TestWrapperMultThreadToClass::Test (ThreadBlockParam* _tb )
Test function 內沒有存取任何一個 class member data. 它須要的資料不是從 _tb 得到. 不然就是自己產生. 所以不會產生 記憶體存取錯誤.
拿掉 (tb + nowThread)->_wrap = (void*) this; 這並不是一個好的寫法. 因為M大的程式 FunctionCalledByThread() 中有用到TestWrapperMultThreadToClass *_this = (_tb->_wrap);
然後呼叫了 _this->Test(_tb) 如沒有寫(tb + nowThread)->_wrap = (void*) this; 那這時的_this 並不是一個合法的值. C++ 的runtime 不會去檢查這個值是否是正確的. 直接把他傳給Test(). 在這個Test() function 中如上面說的沒有去存取class member data. 所以不會出問題. 但某一天. 你改了Test() 去存取class 的member data 時. 就會發生錯誤 . 然後呢.. 你會debug 了了辦天也找不到問題.





作者: MY0613    時間: 2018-7-30 05:05 PM

MSDN 查到
Multithreaded versions of the C run-time libraries only.

To use _beginthread or _beginthreadex, the application must link with one of the multithreaded C run-time libraries.

還真的是 C run-time lib... 難怪要 static function prt.
不過究竟是有什麼特別需求要這樣搞?!(真的實在是不懂)

謝謝提點,受教了。



作者: 龍謙    時間: 2018-7-30 08:40 PM

本帖最後由 龍謙 於 2018-7-30 08:40 PM 編輯
MY0613 發表於 2018-7-30 11:20 AM
其實我不時懂你這邊的意思,但在 static FunctionCalledByThread function 裡面,
我目的是想用 ThreadBlo ...

了解
其實我會將FunctionCalledByThread function宣告成static是因為,一開始使用時,我是直接使用unsigned int __stdcall Test(void* _tb),但VS 2015的編譯器一直報錯,後來上網找了資料才知道,若是要將_beginthreadex包成class,要先用一個static的函式間接去讀取真正要執行的函式,所以我才用static unsigned int __stdcall FunctionCalledByThread(void *ptr_this)去呼叫Test(ThreadBlockParam* _tb),我參考的網站網址如下:
https://stackoverflow.com/questi ... th-member-functions

作者: 龍謙    時間: 2018-7-30 08:45 PM

本帖最後由 龍謙 於 2018-7-30 08:56 PM 編輯
cockroachrun 發表於 2018-7-30 04:01 PM
這程式有bug
bug 發生在這段code這段code 會讓你的ThreadIndex 為 8 . 由你的結果也發現確實會出現8
當 ...

喔喔~~~~了解
感恩c大詳細的解說~~~
在stackoverflow上查到的時候,只知道是要將自己的instance傳給自己,但不知道為什麼,現在知道了,學到好多喔!
感恩大大!

關於那個bug的部分,if判斷式的地方要ThreadIndex < _ThreadNum - 1
因為這樣在ThreadIndex 為7的時候才會歸零。這部分我貼的程式碼中有減,不過可能M大在測試時沒注意到,漏打了。
小弟想問一個額外的問題,如果是在struct中,也要使用static的方式去間接執行要執行的動作嗎?
因為我覺得struct和class滿像的,只是class多了一些保護資料的機制而已,所以突然想到,如果在struct中宣告了一個函式,而這個函式也使用了_beginthreadex,那傳給_beginthreadex的函式也需要是static嗎?


作者: 龍謙    時間: 2018-7-30 08:51 PM

MY0613 發表於 2018-7-30 05:05 PM
從 MSDN 查到

還真的是 C run-time lib... 難怪要 static function prt.

哈哈,我在研究的時候也覺得有點麻煩,不過也因為研究這個學到許多東西,還滿值得的 XD
會使用這個方式是因為我們公司使用廠商寫的函式庫,那個函式庫中的功能我想要用多執行緒優化它;讓它執行快一點,也想讓這個功能的可移植性好一點,所以才想把它包成class。

作者: MY0613    時間: 2018-7-30 11:08 PM

龍謙 發表於 2018-7-30 08:45 PM
喔喔~~~~了解
感恩c大詳細的解說~~~
在stackoverflow上查到的時候,只知道是要將自己的instance傳給自己, ...

好像是被我手賤給砍到了…真是歹勢啦…(下次會只貼 patch)
作者: cockroachrun    時間: 2018-7-31 05:22 PM

本帖最後由 cockroachrun 於 2018-7-31 05:27 PM 編輯
MY0613 發表於 2018-7-30 05:05 PM
從 MSDN 查到

還真的是 C run-time lib... 難怪要 static function prt.

原因1
_beginthreadex 其實只是MS 幫你包裝好讓你在C 當中可以安全的建立thread. 最後他還是會去呼叫CreateThread() 這個 windows API . 這個API 要求的也是一個 c function
原因2
MS 為了提共最大的可用性. 當然要把_beginthreadex 設計成可以在 C 中使用. 可在 C 中使用. 當然也可以在C++ 中使用. 只是對於 叫用class 的member function 會比較麻煩.

或許MS 也可以提供另一個版本. 給C++ 用. 可以使用在 class 的member function. 但為什麼不提供. 這個問題就不清楚了.


如果真的想要用C++ 版本thread 功能. 可以參考標準std library .有 std::thread 可以使用. 這個功能在c++11 版之後就有提供了. VS2015 也有提供.
std::thread 版的demo




歡迎光臨 伊莉討論區 (http://www0123456789.eyny.com/) Powered by Discuz!