天天看点

《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>条件,如下代码所示:

继续阅读