我有一个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:&#39;调用线程无法访问此对象,因为其他线程拥有该对象。&#39;
我理解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:&#39;没有足够的存储空间来处理此命令&#39 39 ;.
由于应用程序没有占用大量内存,我不确定导致该消息的原因。
我意识到这个示例应用程序正在同时启动所有线程,但我的完整应用程序确保只有4个线程正在运行,并且我仍然在一段时间后收到此消息。我不知道为什么,但我也无法摆脱这条消息 - 即使在重新启动应用程序和Visual Studio之后也是如此。对于导致此消息的内容有一点了解是有用的。
所以我的问题是 - 如果我只需要,说4个线程,一次运行 - 有没有办法我可以创建4个VBScriptEngine实例并为每个新线程调用重用它?或者甚至在主线程上只有一个VBScriptEngine实例,每个新线程只分享它?
答案 0 :(得分:1)
使用ClearScript团队中的some help,我能够使用每个线程仅使用一个专用引擎实例来使我的示例应用程序正常工作。诀窍是首先使用自己的线程创建所有必需的引擎,然后在我的循环中使用 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; }
}
答案 1 :(得分:-2)
using
语句允许垃圾收集器(GC)在不再使用该对象时自动释放分配给托管对象的内存,这样您就不会有开销。
对于第一个问题,您需要使用lock
statement,它允许确保一个线程不进入临界区而另一个线程位于代码的关键部分:
private static readonly object locked = new object();
static void ThreadedFunc(object i)
{
// Only one thread will execute this code
lock (locked)
{
// When you exit the Using block, the `VBScriptEngine` is going to be disposed
using (VBScriptEngine vbsengine = new VBScriptEngine())
{
Console.WriteLine(i + ": " + vbsengine.Evaluate("1+1"));
}
}
}
如果您想设置限制,那么您可以尝试:
Parallel.For(0, 4000, new ParallelOptions
{
MaxDegreeOfParallelism = 4 // Thread limit
}, i =>
{
// In this case you don't need to use lock statement, it's already thread safe
ThreadedFunc(i);
});