我有一个C#应用程序,我在其中生成多个线程。我使用的是.NET Framework 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 实例并为每个新线程调用重用它?甚至只是主线程上的 VBScriptEngine 的 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; }
}