科技行者

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

知识库

知识库 安全导航

至顶网软件频道基础软件利用VC# 创作简单的多线程组件

利用VC# 创作简单的多线程组件

  • 扫一扫
    分享文章到微信

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

可以编写能同时执行多个任务的应用程序。此能力是设计处理器密集型且要求用户输入的组件的强大方法

作者:佚名 来源:MSDN 2007年11月13日

关键字:

  • 评论
  • 分享微博
  • 分享邮件
封送处理对控件的调用

  现在将加速窗体上的显示更新。鉴于控件总是由主执行线程所有,从属线程中对控件的任何调用都需要“封送处理”调用。封送处理是跨线程边界移动调用的行为,需要耗费大量的资源。为了使需要发生的封送处理量减到最少,并确保以线程安全方式处理调用,应使用 Control.BeginInvoke 方法来调用主执行线程上的方法,从而使必须发生的跨线程边界的封送处理量减到最少。当调用操作控件的方法时,这种调用非常必要。有关详细信息,请参见从线程操作控件。

  创建控件调用过程

  为 frmCalculations 打开代码编辑器。在声明部分,添加下列代码:

public delegate void FHandler(double Value, double Calculations);
public delegate void A2Handler(int Value, double Calculations);
public delegate void LDHandler(double Calculations, int Count);

  Invoke 和 BeginInvoke 需要将适当方法的委托作为参数。这些代码行声明一些委托签名,这些签名将被 BeginInvoke 用于调用适当的方法。

  在代码中添加下列空方法。

public void FactHandler(double Value, double Calculations)
{
}
public void Fact1Handler(double Value, double Calculations)
{
}
public void Add2Handler(int Value, double Calculations)
{
}
public void LDoneHandler(double Calculations, int Count)
{
}

  在“编辑”菜单中,使用“剪切”和“粘贴”,从 FactorialHandler 方法中剪切所有代码,并将其粘贴到 FactHandler 中。

  对 FactorialMinusHandler 和 Fact1Handler、AddTwoHandler 和 Add2Handler 以及 LoopDoneHandler 和 LDoneHandler 重复上面的步骤。

  完成后,在 FactorialHandler、Factorial1Handler、AddTwoHandler 和 LoopDoneHandler 中应该没有剩余代码,并且它们曾经包含的所有代码应该已经移动到适当的新方法中。

  调用 BeginInvoke 方法以异步调用这些方法。可以从窗体 (this) 或者窗体上的任何控件调用 BeginInvoke。

  完成后,代码看起来应该类似以下形式:

protected void FactorialHandler(double Value, double Calculations)
{
// BeginInvoke causes asynchronous execution to begin at the address
// specified by the delegate. Simply put, it transfers execution of
// this method back to the main thread. Any parameters required by
// the method contained at the delegate are wrapped in an object and
// passed.
this.BeginInvoke(new FHandler(FactHandler), new Object[]
{Value, Calculations});
}
protected void FactorialMinusHandler(double Value, double Calculations)
{
this.BeginInvoke(new FHandler(Fact1Handler), new Object []
{Value, Calculations});
}

protected void AddTwoHandler(int Value, double Calculations)
{
this.BeginInvoke(new A2Handler(Add2Handler), new Object[]
{Value, Calculations});
}

protected void LoopDoneHandler(double Calculations, int Count)
{
this.BeginInvoke(new LDHandler(LDoneHandler), new Object[]
{Calculations, Count});
}

  看起来似乎事件处理程序仅仅是对下一个方法进行调用。实际上,该事件处理程序实现了在主操作线程上调用方法。这种方法可节省跨线程边界的调用,并使多线程应用程序能够有效运行而不必担心导致死锁。有关在多线程环境下使用控件的详细信息,请参见从线程操作控件。

  保存您的工作。

  从“调试”菜单中选择“启动”,测试该解决方案。

  在文本框内键入 10000000 并单击“运行循环”。

  此按钮下方的标签中显示“Looping”。运行这个循环应该占用很长时间。如果它完成得太快,请相应地调整该数字的大小。

  连续地快速单击仍在启用的三个按钮。您会发现所有按钮都响应您的输入。在“Add Two”下方的标签应该第一个显示结果。结果稍后将显示在阶乘按钮下方的标签中。估计这些结果会无限大,因为 10,000,000 的阶乘返回的数字对于双精度变量而言太大,以至超出了它包含的范围。最后,再过片刻,结果将返回到“运行循环”按钮下方。

  正如刚刚观察到的,在四个单独的线程上同时执行四组独立的计算。用户界面保持对输入的响应,并在每个线程完成后返回结果。

  协调线程

  有经验的多线程应用程序用户可能会发现已键入的代码中存在细微缺陷。从 Calculator.cs 中每个执行计算的子例程中撤回以下代码行:

varTotalCalculations += 1;
varTotalAsOfNow = varTotalCalculations;

  这两行代码递增公共变量 varTotalCalculations 并将局部变量 varTotalAsOfNow 设为此值。然后,该值被返回给 frmCalculations,并显示在标签控件中。但返回的值正确吗?如果只有单个执行线程在运行,则答案明显是正确的。但是如果有多个线程在运行,答案则变得不太确定。每个线程都具有递增变量 varTotalCalculations 的能力。有可能出现这样的情况:在一个线程递增该变量之后,但在它将该值复制到 varTotalAsOfNow 之前,另一个线程可能通过递增该变量而更改它的值。这将导致有可能每个线程实际上在报告不正确的结果。Visual C# 提供 lock 语句语句以允许线程的同步,从而确保每个线程始终返回准确的结果。lock 的语法如下所示:

lock(AnObject)
{
// Insert code that affects the object.
// Insert more code that affects the object.
// Insert more code that affects the object.
// Release the lock.
}

  输入 lock 块后,在指定的线程对所讨论的对象拥有专用锁之前,对指定表达式的执行一直被堵塞。在上面显示的示例中,对 AnObject 的执行处于锁定状态。必须对返回引用的对象(而非返回值的对象)使用 lock。然后,执行以块的形式继续进行,而不会受到其他线程的干扰。作为一个单元执行的语句集称为“原子”。当遇到 } 时,表达式将被释放,线程可继续正常工作。

  将 lock 语句添加到应用程序

  在代码编辑器中打开 Calculator.cs。

  找到下列代码的每个实例:

varTotalCalculations += 1;
varTotalAsOfNow = varTotalCalculations;

  应该有此代码的四个实例,每个计算方法中有一个。

  修改此代码,使其显示为如下形式:

lock(this)
{
varTotalCalculations += 1;
varTotalAsOfNow = varTotalCalculations;
}

  保存工作,并按上例所示进行测试。

  您可能注意到对程序性能的细微影响。这是因为当组件获得排他锁后,线程的执行停止。尽管它保证了正确性,但这种方法抵消了多线程带来的某些性能优点。应该认真考虑锁定线程的必要性,并且仅当绝对必要时才予以实现。

查看本文来源

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

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

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