本節書摘來自異步社群出版社《c++多線程程式設計實戰》一書中的第2章,第2.9節,作者: 【黑山共和國】milos ljumovic(米洛斯 留莫維奇),更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。
既可以在使用者空間也可以在核心中實作線程包。具體選擇在哪裡實作還存在一些争議,在一些實作中可能會混合使用核心線程和使用者線程。
我們将讨論在不同地方實作線程包的方法及優缺點。第1種方法是,把整個線程包放進使用者空間,核心完全不知道。就核心而言,它管理着普通的單線程程序。這種方法的優點和最顯著的優勢是,可以在不支援線程的作業系統中實作使用者級線程包。
過去,傳統的作業系統就采用這種方法,甚至沿用至今。用這種方法,線程可以通過庫來實作。所有這些實作都具有相同的通用結構。線程運作在運作時系統的頂部,該系統是專門管理線程的過程集合。我們在前面見過一些例子(<code>createthread</code>、<code>terminatethread</code>等),以後還會見到更多。
下面的程式示例示範了在使用者空間中的線程用法。我們要複制大型檔案,但是不想一開始就讀取整個檔案的内容,或者更優化地一部分一部分地讀取,而且不用在檔案中寫入資料。這就涉及2.5節中提到的生産者-消費者問題。
準備就緒
确定安裝并運作了visual studio。
操作步驟
1. 建立一個新的win32應用程式項目,并命名為<code>concurrentfilecopy</code>。
2. 打開【解決方案資料總管】,添加一個新的頭檔案,命名為<code>concurrentfilecopy.h</code>。打開<code>concurrentfilecopy.h</code>,并輸入下面的代碼:
tchar* sztitle = _t("concurrent file copy");
tchar* szwindowclass = t(" _cfc_wnd_class_ _");
dword dwreadbytes = 0;
dword dwwritebytes = 0;
dword dwblocksize = 0;
dword dwfilesize = 0;
hlocal pmemory = null;
int winapi _twinmain(hinstance hinstance, hinstance hprev, lptstr
szcmdline, int icmdshow)
{
unreferenced_parameter(hprev);
unreferenced_parameter(szcmdline);
registerwndclass(hinstance);
hwnd hwnd = null;
hwnd hwndpb = null;
if (!(hwnd = initializeinstance(hinstance, icmdshow, hwndpb)))
{
return 1;
}
msg msg = { 0 };
tchar szreadfile[max_path];
tchar szwritefile[max_path];
if (filedialog(hwnd, szreadfile, file_open) && filedialog(hwnd,
szwritefile, file_save))
copydetails copydetails = { hinstance, hwndpb, szreadfile,
szwritefile };
handle hmutex = createmutex(null, false, mutex_name);
handle hreadthread = createthread(null, 0,
(lpthread_start_routine)readroutine, &copydetails, 0, null);
while (getmessage(&msg, null, 0, 0))
{
translatemessage(&msg);
dispatchmessage(&msg);
}
closehandle(hreadthread);
closehandle(hmutex);
else
messagebox(hwnd, _t("cannot open file!"),
_t("error!"), mb_ok);
localfree(pmemory);
unregisterclass(szwindowclass, hinstance);
return (int)msg.wparam;
}
atom registerwndclass(hinstance hinstance)
wndclassex wndex;
wndex.cbsize = sizeof(wndclassex);
wndex.style = cs_hredraw | cs_vredraw;
wndex.lpfnwndproc = wndproc;
wndex.cbclsextra = 0;
wndex.cbwndextra = 0;
wndex.hinstance = hinstance;
wndex.hicon = loadicon(hinstance, makeintresource(idi_application));
wndex.hcursor = loadcursor(null, idc_arrow);
wndex.hbrbackground = (hbrush)(color_window + 1);
wndex.lpszmenuname = null;
wndex.lpszclassname = szwindowclass;
wndex.hiconsm = loadicon(wndex.hinstance, makeintresource(idi_application));
return registerclassex(&wndex);
hwnd initializeinstance(hinstance hinstance, int icmdshow, hwnd& hwndpb)
hwnd hwnd = createwindow(szwindowclass, sztitle, ws_overlapped
| ws_caption | ws_sysmenu | ws_minimizebox, 200, 200, 440, 290,
null, null, hinstance, null);
rect rcclient = { 0 };
int cyvscroll = 0;
if (!hwnd)
return null;
hfont hfont = createfont(14, 0, 0, 0, fw_normal, false, false,
false, baltic_charset, out_default_precis, clip_default_precis,
default_quality, default_pitch | ff_modern,
_t("microsoft sans serif"));
hwnd hbutton = createwindow(_t("button"), _t("close"), ws_child
| ws_visible | bs_pushbutton | ws_tabstop, 310, 200, 100, 25,
hwnd, (hmenu)button_close, hinstance, null);
sendmessage(hbutton, wm_setfont, (wparam)hfont, true);
getclientrect(hwnd, &rcclient);
cyvscroll = getsystemmetrics(sm_cyvscroll);
hwndpb = createwindow(progress_class, (lptstr)null, ws_child |
ws_visible, rcclient.left, rcclient.bottom - cyvscroll,
rcclient.right, cyvscroll, hwnd, (hmenu)0, hinstance, null);
sendmessage(hwndpb, pbm_setstep, (wparam)1, 0);
showwindow(hwnd, icmdshow);
updatewindow(hwnd);
return hwnd;
lresult callback wndproc(hwnd hwnd, uint umsg, wparam wparam, lparam lparam)
switch (umsg)
case wm_command:
switch (loword(wparam))
{
case button_close:
{
destroywindow(hwnd);
break;
}
}
break;
case wm_destroy:
postquitmessage(0);
default:
return defwindowproc(hwnd, umsg, wparam, lparam);
return 0;
dword winapi readroutine(lpvoid lpparameter)
pcopydetails pcopydetails = (pcopydetails)lpparameter;
handle hfile = createfile(pcopydetails->szreadfilename,
generic_read, file_share_read, null, open_existing,
file_attribute_normal, null);
if (hfile == (handle)invalid_handle_value)
return false;
dwfilesize = getfilesize(hfile, null);
dwblocksize = getblocksize(dwfilesize);
handle hwritethread = createthread(null, 0,
(lpthread_start_routine)writeroutine, pcopydetails, 0, null);
size_t ubufferlength = (size_t)ceil((double) dwfilesize / (double)dwblocksize);
sendmessage(pcopydetails->hwndpb, pbm_setrange, 0,
makelparam(0, ubufferlength));
pmemory = localalloc(lptr, dwfilesize);
void* pbuffer = localalloc(lptr, dwblocksize);
int ioffset = 0;
dword dwbytesred = 0;
do
readfile(hfile, pbuffer, dwblocksize, &dwbytesred, null);
if (!dwbytesred)
handle hmutex = openmutex(mutex_all_access, false,
mutex_name);
waitforsingleobject(hmutex, infinite);
memcpy((char*)pmemory + ioffset, pbuffer, dwbytesred);
dwreadbytes += dwbytesred;
releasemutex(hmutex);
ioffset += (int)dwblocksize;
} while (true);
localfree(pbuffer);
closehandle(hfile);
closehandle(hwritethread);
dword winapi writeroutine(lpvoid lpparameter)
handle hfile = createfile(pcopydetails->szwritefilename,
generic_write, 0, null, create_always, file_attribute_normal, null);
dword dwbyteswritten = 0;
int iremainingbytes = (int)dwfilesize - ioffset;
if (iremainingbytes <= 0)
sleep(10);
if (dwwritebytes < dwreadbytes)
dword dwbytestowrite = dwblocksize;
if (!(dwfilesize / dwblocksize))
dwbytestowrite = (dword)iremainingbytes;
handle hmutex = openmutex(mutex_all_access, false, mutex_name);
waitforsingleobject(hmutex, infinite);
writefile(hfile, (char*)pmemory + ioffset, dwbytestowrite,
&dwbyteswritten, null);
dwwritebytes += dwbyteswritten;
releasemutex(hmutex);
sendmessage(pcopydetails->hwndpb, pbm_stepit, 0, 0);
ioffset += (int)dwblocksize;
bool filedialog(hwnd hwnd, lptstr szfilename, dword dwfileoperation)
openfilenamew ofn;
openfilenamea ofn;
tchar szfile[max_path];
zeromemory(&ofn, sizeof(ofn));
ofn.lstructsize = sizeof(ofn);
ofn.hwndowner = hwnd;
ofn.lpstrfile = szfile;
ofn.lpstrfile[0] = '0';
ofn.nmaxfile = sizeof(szfile);
ofn.lpstrfilter = _t("all0.0text0*.txt0");
ofn.nfilterindex = 1;
ofn.lpstrfiletitle = null;
ofn.nmaxfiletitle = 0;
ofn.lpstrinitialdir = null;
ofn.flags = dwfileoperation == file_open ? ofn_pathmustexist |
ofn_filemustexist : ofn_showhelp | ofn_overwriteprompt;
if (dwfileoperation == file_open)
if (getopenfilename(&ofn) == true)
_tcscpy_s(szfilename, max_path - 1, szfile);
return true;
if (getsavefilename(&ofn) == true)
return false;
dword getblocksize(dword dwfilesize)
return dwfilesize > 4096 ? 4096 : 512;
}<code>`</code>
示例分析
我們建立了一個和哲學家就餐示例非常像的ui。例程<code>myregisterclass</code>、<code>initin</code>stance和<code>wndproc</code>幾乎都一樣。我們在程式中添加<code>filedialog</code>來詢問使用者讀寫檔案的路徑。為了讀和寫,分别啟動了兩個線程。
作業系統的排程十分複雜。我們根本不知道是排程算法還是硬體中斷使得某線程被排程在cup中執行。這意味着寫線程可能在讀線程之前執行。出現這種情況會導緻一個異常,因為寫線程沒東西可寫。
是以,我們在寫操作中添加了<code>if</code>條件,如下代碼所示: