科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件实例解析C++/CLI线程之多任务

实例解析C++/CLI线程之多任务

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

从处理器的角度来看,线程是一个单独的执行流程,每个线程都有各自的寄存器及堆栈上下文。

作者:谢启东编译 来源:天极开发 2007年11月14日

关键字:

  • 评论
  • 分享微博
  • 分享邮件
请看例2中的ThreadY类,当一个线程调用标记1中的Move,而另一个线程隐式地调用标记2中的ToString时,潜在的冲突就发生了。因为两个函数没有用同步措施来访问同一个Point,Move可能会先更新x坐标,但在它更新相应的y坐标之前,ToString却显示了一对错误的坐标值,这时,输出可能会如插2a所示。然而,当相关的语句被同步之后,ToString显示的坐标对总是正确匹配的,同步执行之后的输出如插2b所示。再看一下例2中的Point类型,在此可看到这些访问x与y坐标的函数是如何被同步的。

  插2:a线程输出产生了不匹配的坐标对;b同步执行中匹配的坐标对

  (a)

(1878406,1878406)
(2110533,2110533)
(2439367,2439367)
(2790112,2790112)
x: 3137912
y: 3137911 // y与x不同
(3137912,3137911) // y与x不同
(3466456,3466456)
(3798720,3798720)
(5571903,5571902) // y与x不同
(5785646,5785646)
(5785646,5785646)

  (b)

(333731,333731)
(397574,397574)
(509857,509857)
(967553,967553)
x: 853896
y: 967553 // y仍与x不同
(1619521,1619521)
(1720752,1720752)
(1833313,1833313)
(2973291,2973291)
(3083198,3083198)
(3640996,3640996)

  在此,可把一段语句放在一个称作"同步锁"--即Thread::Monitor的Enter与Exit语句当中,来进行对某些资源的独占式访问,如标记1a与1b、2a与2b、3a与3b、4a与4b。

  因为Move与ToString都是实例函数,当它们在同一Point上被调用时,它们共享Point的同步锁,为独占访问一个对象,就必须传递一个指向对象的句柄给Enter。如果在ToString访问时,Move也被调用操作同一Point,Move将会一直处于阻塞状态,直至ToString完成,反之亦然。结果就是,函数花费时间在相互等待,反之没有同步,它们都会尽可能快地同时运行。

  一旦同步锁控制了对象,它将保证在同一时刻,只有一个此类的实例函数可以在对象上执行它的关键代码。当然,类中没有使用同步锁的其他实例函数,可不会理会它的同步"兄弟"在做些什么,所以,必须小心适当地使用同步锁(注意,X与Y的访问器未被同步)。同步锁对于那些操作不同对象的实例函数,将不起任何作用,这些函数不会互相等待。

  通常地,当调用Exit时,同步锁就被释放了,因此,同步锁的作用范围就是Enter与Exit中间的那些代码,程序员必须有责任避免死锁问题的发生--防止线程A一直等待线程B,或反之。

  假设有一个包含25条语句的函数,其中只有3条连贯的语句需要同步,如果我们把全部的25条语句都包括在一个同步锁中,那么,将把资源比实际所需锁住了更长的时间。正如前述代码所示,每个同步锁保持的时间都要尽可能地短。

  请看例3中的ArrayManip结构,当同步锁执行到标记2时,锁中的array正处于忙碌状态,因此将会阻塞其他所有在array上需要同步的代码。

  例3:

using namespace System;
using namespace System::Threading;

public ref struct ArrayManip
{
 static int TotalValues(array<int>^ array)
 {
  /*1*/ int sum = 0;
  /*2*/ Monitor::Enter(array);
  {
   for (int i = 0; i < array->Length; ++i)
   {
    sum += array[i];
   }
  }
  Monitor::Exit(array);
  return sum;
 }

 static void SetAllValues(array<int>^ array, int newValue)
 {
  /*3*/ Monitor::Enter(array);
  {
   for (int i = 0; i < array->Length; ++i)
   {
    array[i] = newValue;
   }
  }
  Monitor::Exit(array);
 }

 static void CopyArrays(array<int>^ array1, array<int>^ array2)
 {
  /*4*/ Monitor::Enter(array1);
  {
   /*5*/ Monitor::Enter(array2);
   {
    Array::Copy(array1, array2,
     array1->Length < array2->Length ? array1->Length
     : array2->Length);
   }
   Monitor::Exit(array2);
  }
  Monitor::Exit(array1);
 }
};

  一个同步锁可包含同一对象的另一个同步锁,在这种情况下,锁计数相应地增长了;但如果想被另一个线程中的同步语句操作,必须先递减到零。一个同步锁还可包含不同对象的同步锁,在此情况下,它将会一直阻塞,直到第二个对象可访问,函数CopyArrays就是一个例子。
    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章