天天看點

《C++多線程程式設計實戰》——2.9 在使用者空間實作線程include "ConcurrentFileCopy.h"ifdef _UNICODEelseendif

本節書摘來自異步社群出版社《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) &amp;&amp; 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, &amp;copydetails, 0, null);

    while (getmessage(&amp;msg, null, 0, 0))

    {

      translatemessage(&amp;msg);

      dispatchmessage(&amp;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(&amp;wndex);

hwnd initializeinstance(hinstance hinstance, int icmdshow, hwnd&amp; 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, &amp;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-&gt;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-&gt;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, &amp;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-&gt;szwritefilename,

    generic_write, 0, null, create_always, file_attribute_normal, null);

  dword dwbyteswritten = 0;

    int iremainingbytes = (int)dwfilesize - ioffset;

    if (iremainingbytes &lt;= 0)

    sleep(10);

    if (dwwritebytes &lt; 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,

        &amp;dwbyteswritten, null);

      dwwritebytes += dwbyteswritten;

      releasemutex(hmutex);

      sendmessage(pcopydetails-&gt;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(&amp;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(&amp;ofn) == true)

      _tcscpy_s(szfilename, max_path - 1, szfile);

      return true;

    if (getsavefilename(&amp;ofn) == true)

  return false;

dword getblocksize(dword dwfilesize)

  return dwfilesize &gt; 4096 ? 4096 : 512;

}<code>`</code>

示例分析

我們建立了一個和哲學家就餐示例非常像的ui。例程<code>myregisterclass</code>、<code>initin</code>stance和<code>wndproc</code>幾乎都一樣。我們在程式中添加<code>filedialog</code>來詢問使用者讀寫檔案的路徑。為了讀和寫,分别啟動了兩個線程。

作業系統的排程十分複雜。我們根本不知道是排程算法還是硬體中斷使得某線程被排程在cup中執行。這意味着寫線程可能在讀線程之前執行。出現這種情況會導緻一個異常,因為寫線程沒東西可寫。

是以,我們在寫操作中添加了<code>if</code>條件,如下代碼所示:

繼續閱讀