我正在 Parallel.ForEach 中调用 VB 6.0 dll,并期望所有调用同时启动,或者根据我的 PC 核心或线程池中的线程可用性至少启动其中 2 个调用
VB6 动态链接库
Public Function DoJunk(ByVal counter As Long, ByVal data As String) As Integer
Dim i As Long
Dim j As Long
Dim s As String
Dim fno As Integer
fno = FreeFile
Open "E:\JunkVB6Dll\" & data & ".txt" For Output Access Write As #fno
Print #fno, "Starting loop with counter = " & counter
For i = 0 To counter
Print #fno, "counting " & i
Next
Close #fno
DoJunk = 1
End Function
计数器从调用者处传递来控制调用的执行时间,并且正在写入文件以使其成为基于 IO 的进程。
C# 调用者
private void ReportProgress(int value)
{
progressBar.Value = value;
//progressBar.Value++;
}
private void button1_Click(object sender, EventArgs e)
{
progressBar.Value = 0;
counter = 0;
Stopwatch watch = new Stopwatch();
watch.Start();
//var range = Enumerable.Range(0, 100);
var range = Enumerable.Range(0, 20);
bool finished = false;
Task.Factory.StartNew(() =>
{
Parallel.ForEach(range, i =>
{
#region COM CALL
JunkProject.JunkClass junk = new JunkProject.JunkClass();
try
{
Random rnd = new Random();
int dice = rnd.Next(10, 40);
int val = 0;
if (i == 2)
val = junk.DoJunk(9000000, i.ToString());
else
val = junk.DoJunk(dice * 10000, i.ToString());
System.Diagnostics.Debug.Print(junk.GetHashCode().ToString());
if (val == 1)
{
Interlocked.Increment(ref counter);
progressBar.Invoke((Action)delegate { ReportProgress(counter); });
}
junk = null;
}
catch (Exception excep)
{
i = i;
}
finally { junk = null; }
#endregion
});
}).ContinueWith(t =>
{
watch.Stop();
MessageBox.Show(watch.ElapsedMilliseconds.ToString());
});
}
此线路拨打特定电话的时间比其他线路长。
val = junk.DoJunk(9000000, i.ToString());
这里第二个进程导致 Parallel.ForEach 内的所有调用停止,即除非第二个调用完成,否则不会创建其他文件。
这是预期的行为还是我做错了什么?
正如 @John Wu 建议您可以创建 AppDomain 以允许 COM 在不同的 App Domain 上运行,我相信您可以像这样运行并行。
Parallel.ForEach(range, i =>
{
AppDomain otherDomain = AppDomain.CreateDomain(i.ToString());
otherDomain.DoCallBack(delegate
{
//Your COM call
});
});
编辑
对..我不知道如何在 VB6.0 类上设置可序列化。您可以尝试其他方法(通过引用封送对象)。注意:我还没有实际测试过这个,但我想知道这是否有效。
Parallel.ForEach(range, i =>
{
AppDomain otherDomain = AppDomain.CreateDomain(i.ToString());
var comCall = (ComCall) otherDomain.CreateInstanceFromAndUnwrap(Assembly.GetExecutingAssembly().Location, typeof(ComCall).ToString());
comCall.Run();
AppDomain.Unload(otherDomain);
});
和班级
public class ComCall : MarshalByRefObject
{
public void Run()
{
//Your COM Call
}
}
这里还有有关该主题的其他参考。
https://www.codeproject.com/Articles/14791/NET-Remoting-with-an-easy-example