以下列课程为例。
public class A
{
// ...
void Foo(S myStruct){...}
}
public class B
{
public A test;
// ...
void Bar()
{
S myStruct = new S();
test.Foo(myStruct);
}
}
现在,我希望方法调用test.Foo(myStruct)成为异步调用('fire-and-forget')。条形方法需要尽快返回。代理,BeginInvoke,EndInvoke,ThreadPool等文档不能帮助我找到解决方案。
这是一个有效的解决方案吗?
// Is using the `EndInvoke` method as the callback delegate valid?
foo.BeginInvoke(myStruct, foo.EndInvoke, null);
答案 0 :(得分:12)
您无需致电EndInvoke
;不称它只是意味着:
听起来你想要“发射并忘记”,所以最简单的方法是使用匿名委托,例如:
var del = new Action(foo.Bar);
del.BeginInvoke(iar =>
{
try
{
del.EndInvoke(iar);
}
catch (Exception ex)
{
// Log the message?
}
}, null);
执行此代码时会发生这种情况:
del
和匿名代表(iar => ...
)。del
。EndInvoke
时,将返回该方法的结果,或者抛出异常(如果发生了异常)。请注意,上述示例与以下内容非常不同:
// This is pointless and is still, essentially, synchronous.
del.EndInvoke(del.BeginInvoke(null, null));
修改:您应该始终致电End*
。我从来没有找到一个没有调用它会出现问题的场景,但这是一个实现细节,并且是relying on undocumented behavior.
最后,如果抛出异常,你的解决方案会使进程崩溃,如果你不关心异常(所以你要找的最后一个例子可能是:del.BeginInvoke(myStruct, null, null);
),你可以简单地将null作为委托传递。
public class A
{
// ...
void Foo(S myStruct){...}
void FooAsync(S myStruct)
{
var del = new Action<S>(Foo);
del.BeginInvoke(myStruct, SuppressException, del);
}
static void SuppressException(IAsyncResult ar)
{
try
{
((Action<S>)ar.AsyncState).EndInvoke(ar);
}
catch
{
// TODO: Log
}
}
}
答案 1 :(得分:2)
我想说你最好的选择是使用ThreadPool
:
void bar()
{
ThreadPool.QueueUserWorkItem(o=>
{
S myStruct = new S();
test.foo(myStruct);
});
}
这会将片段排队以便在单独的线程中执行。现在你还需要注意其他事项:如果你有多个线程访问同一个A
实例并且该实例修改了一个变量,那么你必须确保你对变量进行适当的同步。
public class A
{
private double sum;
private volatile bool running;
private readonly object sync;
public A()
{
sum = 0.0;
running = true;
sync = new object();
}
public void foo(S myStruct)
{
// You need to synchronize the whole block because you can get a race
// condition (i.e. running can be set to false after you've checked
// the flag and then you would be adding the sum when you're not
// supposed to be).
lock(sync)
{
if(running)
{
sum+=myStruct.Value;
}
}
}
public void stop()
{
// you don't need to synchronize here since the flag is volatile
running = false;
}
}
答案 2 :(得分:1)
您可以使用@ What is AsyncCallback?
解释的回调模型这样你的EndInvoke不会在bar()中,而是在一个单独的回调方法中。
在示例中,EndRead(对应于EndInvoke的回调方法称为CompleteRead,而不是与bar对应的调用方法TestCallbackAPM)
答案 3 :(得分:0)
这是一个选项:
ThreadPool.QueueUserWorkItem(bcl =>
{
var bcList = (List<BarcodeColumn>)bcl;
IAsyncResult iftAR = this.dataGridView1.BeginInvoke((MethodInvoker)delegate
{
int x = this.dataGridView1.Rows[0].Cells.Count - 1;
for (int i = 0; i < this.dataGridView1.Rows.Count - 1; i++)
{
try
{
string imgPath = bcList[i].GifPath;
Image bmpImage = Image.FromFile(imgPath);
this.dataGridView1.Rows[i].Cells[x].Value =bmpImage;
}
catch (Exception)
{
continue;
}
}
});
while (!iftAR.IsCompleted) { /* wait this*/ }
}, barcodeList);