这是VB6线程模型的正确C#实现吗?

时间:2018-04-05 18:22:38

标签: c# multithreading vb6 vb6-migration

我已经阅读了VB6的线程模型,发现这个link非常有用。

考虑到以下几点......

  

VB6事件处理程序是否在不同的线程中运行?

     

不是真的,因为没有单独的线程。 您的代码在单个线程上运行,包含在我上面描述的类似服务的体系结构中。您与之交谈的大部分内容都是其他具有自己公寓的COM对象。所以要来回沟通,当线程互相交谈时,你基本上都在进行RPC调用:你不是直接操纵它们。

     

除此之外,VB6程序有一个定时器,每4秒钟就会唤醒一次,操纵一些全局变量并重新进入睡眠状态,而主程序正在做它的事情。我无法理解为什么这不会导致碰撞。

     

“计时器”位于为计时器创建的单独线程上,但是当它调用代码时,保证不会中断任何其他函数,因为函数调用基本上是一次排队一个线程

...我试图在下面的代码中实现VB6的事件处理行为。

ActionManager.cs

public class ActionManager : IDisposable
{
    private readonly BlockingCollection<Action> ActionQueue = new BlockingCollection<Action>(new ConcurrentQueue<Action>());

    public ActionManager()
    {

    }

    public void Kickoff()
    {
        // Start consumer thread
        new Thread(ExecuteLoop)
        {
            IsBackground = true
        }.Start();
    }

    public void AddAction(Action action)
    {
        ActionQueue.Add(action);
    }

    private void ExecuteLoop()
    {
        // Blocks until new actions are available
        foreach (var action in ActionQueue.GetConsumingEnumerable())
        {
            action.Invoke();
        }
    }

    public void Dispose()
    {
        ActionQueue.CompleteAdding();
        ActionQueue.Dispose();
    }
}

MainForm.cs

public partial class MainForm : Form
{
    public ActionManager actionManager = new ActionManager();

    public MainForm()
    {
        InitializeComponent();
    }

    private void MainForm_Load()
    {
        // Perform preparatory steps, such as initializing resources,
        // configuring settings, etc.
        // (Insert preparatory steps here)

        // Once preparatory steps are complete, start the ActionManager
        actionManager.Kickoff();
    }

    // Event handler for when the Timer's specified interval has elapsed
    private void Timer_Tick(object sender, EventArgs e)
    {
        actionManager.AddAction(() => {
            // (Insert timer event steps here)
        });
    }

    // Event handler for when SomeButton is clicked
    private void SomeButton_Click(object sender, EventArgs e)
    {
        actionManager.AddAction(() => {
            // (Insert button click event steps here)
        });
    }
}

ActionManager通过一个接一个地执行每个事件来管理事件队列。任何类型的事件,例如鼠标点击,计时器滴答,网络分组到达等,将它们各自的事件处理代码排入事件队列。这样,代码将“在单个线程上”运行,这也将处理不同步的全局变量的问题。

这是正确的实施吗?请分享您的想法!

2 个答案:

答案 0 :(得分:4)

如果您要从头开始编写自己的UI框架,那么您所拥有的是自定义消息循环的一个不错的起点。但是你使用的是winforms,你不是从头开始编写自己的UI框架。 Winforms已经拥有自己的消息循环来处理消息,以及一种调度工作在该循环中运行的机制。您无需从头开始创建任何内容。从winforms控件触发的所有事件将已经在UI线程中触发,因此您不需要创建自己的特殊UI线程并管理调度操作。

实际上这样做会导致问题,因为你最终会得到winforms用来管理其UI对象的UI线程,并且你将拥有你正在创建的第二个线程。如果您在该线程中使用过任何UI控件,那么它们会被破坏,因为它们只能在winforms UI线程中使用。

答案 1 :(得分:1)

(我想我首先应该在评论中询问我对遗留应用程序的怀疑是否正确。)

好的,坏消息的时间:你不应该这样做。拜托,拜托,请不要这样做。我告诉你,作为一名开发人员,如果你试图沿着这条路走下去,这将 NOT 结束。

这里发生了什么。您有一个遗留应用程序 - 它可能会对公司非常重要的很多

但问题是,它可能编写得不是很好,它有点胡思乱想,并且端口非常适合现代.NET世界。

现在,你可以尝试沿着破坏.NET的方式进入世界的VB6模式......但是你所做的就是把它带到路上。您仍然需要一个写得不好的,糟糕的遗留应用程序,而您仍然需要维护 - 更糟糕的是,您还必须维护.NET-to-VB6-threading-approach方法

我可以保证你正确的方法是重新设计/重新架构它。写出它的作用,问问自己是否有什么可以改进的过程,并在.NET中从头开始编写。有几个原因:

  1. 您将拥有更稳定的最终产品
  2. 您将花费更少的时间来维护新产品
  3. 无论如何,你最终都必须重新架构该计划。
  4. 如果它有帮助,让我告诉你一个我曾经做过的旧工作的故事。我和同事都负责将VB6应用程序移植到.NET中。他有一个轮胎检查应用程序,我有一个橡胶混合应用程序。

    • 他尝试将他现有的VB6应用程序移植到.NET中,获得所有语言 差异化,GUI /线程问题改变等等
    • 我和用户区的代表坐了下来,然后继续前进 重写橡胶混合应用程序。

    ...我比同事做得更快,我的应用程序更加用户友好,而且维护问题更少了。

    管理层可能喜欢听取您应重写整件事的建议。但是你需要努力争取这个。如果有帮助,请指出大多数软件开发时间不是新编码,而是维护现有软件。可能需要花费更多时间才能重写(即使这不是一个给定的),但从长远来看,它会很快收回成本。