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

Is this a correct C# implementation of VB6's threading model?

我读过 VB6 的线程模型,发现这个 link 很有用。

牢记以下几点...

Do VB6 event handlers run in separate threads?

Not really, because there aren't separate threads. Your code runs on a single thread, wrapped in the service-like architecture I described above. Most of what you talk to that is threaded is other COM objects which have their own apartments. So to communicate back and forth, you are basically doing RPC calls when the threads talk to each other: you aren't directly manipulating them.

Among other things, the VB6 program had a timer that woke up every 4 seconds, manipulated some global variables and went back to sleep, while the main program was doing its thing. I can't understand why this didn't result in collisions.

The "timer" is on a separate thread created for the timer, but when it calls into your code, you are guaranteed not to interrupt any other functions, because the function calls are basically queued one at a time in the thread.

...我试图在下面的代码中实现 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 通过一个接一个地执行每个事件来管理事件队列。任何类型的事件,例如鼠标点击、计时器滴答、网络数据包到达等,都会将它们各自的事件处理代码排入事件队列。这样,代码将 运行 “在单个线程上”,这也将处理非同步全局变量的问题。

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

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

事实上,这样做会导致 问题 ,因为您最终会拥有 winforms 用来管理其 UI 对象的 UI 线程,您将拥有正在创建的第二个线程。如果您曾经在该线程中使用过任何 UI 控件,事情就会中断,因为它们被设计为仅在 winforms UI 线程中使用。

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

好吧,坏消息来了:你不应该这样做。拜托,拜托,拜托,不要这样做。作为一名身处困境的开发人员,我要告诉您,如果您尝试走这条路,这 不会 有好结果。

这是正在发生的事情。您有一个遗留应用程序 - 它可能做了 很多 对公司非常重要的事情。

但问题是,它可能写得不是很好,很古怪,而且没有很好地移植到现代 .NET 世界。

现在,您可以尝试沿着鞋拔 .NET 的道路进入世界的 VB6 模型......但您所做的只是把罐头踢到路上。您仍然有一个 badly-written,古怪的遗留应用程序,您仍然需要维护 - 更糟糕的是,您还必须维护 .NET-to-VB6-threading-approach .

我可以保证你Redesign/Rearchitect它是正确的方法。写下它的作用,问问自己是否可以做些什么来改进流程,然后在 .NET 中从头开始编写。几个原因:

  1. 您将拥有更稳定的最终产品
  2. 您将花费更少的时间来维护新产品
  3. 无论如何,您最终都必须重新设计程序。

如果有帮助,让我告诉你一个我以前工作的故事。我和一位同事都负责将 VB6 应用程序移植到 .NET 中。他有一个轮胎检查应用程序,我有一个橡胶混合应用程序。

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

...我比同事完成得快得多,我的应用程序要多得多 user-friendly,而且维护问题要少得多。

管理层可能喜欢听取您应该重写整个内容的建议。但是你需要为此而努力和奋斗。如果有帮助,请指出大多数软件开发时间不是花在新编码上,而是花在维护现有软件上。重写它可能需要更多的时间(即使这不是给定的)但它会在很长一段时间内很快收回成本 运行.