1

我有一个生成多个线程的 C# 应用程序。我在 .NET 框架 4.7.1 上。在这些线程中,执行工作并且该工作可以执行用户定义的脚本函数。我使用 ClearScript 作为脚本引擎,出于这个问题的目的,我使用的是 VBScriptEngine。这是一个演示我的问题的示例应用程序:

    static VBScriptEngine vbsengine = new VBScriptEngine();

    static void Main(string[] args)
    {
        for (int i=0;i<4000;i++)
        {
            Thread t = new Thread(Program.ThreadedFunc);
            t.Start(i);
        }
        Console.ReadKey();
    }

    static void ThreadedFunc(object i)
    {
        Console.WriteLine(i + ": " + vbsengine.Evaluate("1+1"));
    }

Evaluate()函数中,我收到以下错误: System.InvalidOperationException: “调用线程无法访问此对象,因为不同的线程拥有它。”

我了解 ClearScript 已实现线程亲和性,并且衍生线程无法访问全局定义的引擎。那么我的替代方案是什么?为每个新线程创建一个新的 ClearScript 实例?这似乎非常浪费,并且会产生大量开销——我的应用程序将需要处理数千个线程。无论如何,我继续尝试了这种方法 - 虽然它确实有效(一段时间) - 最终得到了一个错误。这是我的示例应用程序的修订版:

    static void Main(string[] args)
    {
        for (int i=0;i<4000;i++)
        {
            Thread t = new Thread(Program.ThreadedFunc);
            t.Start(i);
        }
        Console.ReadKey();
    }

    static void ThreadedFunc(object i)
    {
        using (VBScriptEngine vbsengine = new VBScriptEngine())
        {
            Console.WriteLine(i + ": " + vbsengine.Evaluate("1+1"));
        }
    }

新的 VBScriptEngine()调用中,我现在开始得到:System.ComponentModel.Win32Exception: '没有足够的存储空间来处理这个命令'。

我不确定是什么导致了该消息,因为应用程序没有占用大量 RAM。

我意识到这个示例应用程序正在同时启动所有线程,但我的完整应用程序确保只有 4 个线程在运行,并且我仍然会在一段时间后收到此消息。我不知道为什么,但我也无法摆脱这条消息——即使在重新启动应用程序和 Visual Studio 之后也是如此。稍微澄清一下导致此消息的原因会很有用。

所以我的问题是 - 如果我只需要,比如说 4 个线程,一次运行 - 有没有办法我可以创建 4 个 VBScriptEngine 实例并将其重用于每个新线程调用?甚至主线程上只有 1 个 VBScriptEngine 实例,每个新线程都共享它?

4

1 回答 1

1

在ClearScript 团队的帮助下,我能够让我的示例应用程序使用每个线程仅使用 1 个专用引擎实例来工作。诀窍是首先使用自己的线程创建所有必要的引擎,然后在我的循环中使用Dispatcher.Invoke()调用我的线程函数。这是使用此方法的更新示例应用程序:

    static void Main(string[] args)
    {
        var vbengines = new VBScriptEngine[Environment.ProcessorCount];
        var checkPoint = new ManualResetEventSlim();
        for (var index = 0; index < vbengines.Length; ++index)
        {
            var thread = new Thread(indexArg =>
            {
                using (var engine = new VBScriptEngine())
                {
                    vbengines[(int)indexArg] = engine;
                    checkPoint.Set();
                    Dispatcher.Run();
                }
            });
            thread.Start(index);
            checkPoint.Wait();
            checkPoint.Reset();
        }

        Parallel.ForEach(Enumerable.Range(0, 4000), item => {
            var engine = vbengines[item % vbengines.Length];

            engine.Dispatcher.Invoke(() => {
                ThreadedFunc(new myobj() { vbengine = engine, index = item });
            });
        });

        Array.ForEach(vbengines, engine => engine.Dispatcher.InvokeShutdown());

        Console.ReadKey();            
    }

    static void ThreadedFunc(object obj)
    {            
        Console.WriteLine(((myobj)obj).index.ToString() + ": " + ((myobj)obj).vbengine.Evaluate("1+1").ToString());
    }

    class myobj
    {
        public VBScriptEngine vbengine { get; set; }
        public int index { get; set; }
    }
于 2018-05-08T18:59:28.127 回答