我需要并行运行5种算法,每种算法都将图像作为输入,并将图像作为输出。完成上述每个操作后,我需要显示5个输出图像。我正在使用代理执行异步回调来完成此任务。
所以,我为这5个algos创建了5个委托,并将它们称为algo1Delegate.BeginInvoke()。
算法运行正常并且也提供输出。我在显示这些图像时面临两个问题。
为了显示图像,我创建了一个ImageViewer类(其中包含带有picturebox元素的窗体)。
//ImageViewer constructor
ImageViewer(Image img, String Title)
{
this.pictureBox1.Image = img;
this.Text = Title;
}
我正在显示这样的图片:
void showImage(Image image, String title)
{
ImageViewer imageviewer = new ImageViewer(image, title);
imageviewer.Show();
}
因为我需要在算法之后显示图像。我正在为每个BeginInvoke()传递new AsyncCallback(showImage)
委托作为第三个参数
private void showImage(IAsyncResult iasycResult)
{
MessageBox.Show("white" + Thread.CurrentThread.ManagedThreadId);
// Retrieve the `caller` delegate.
AsyncResult asycResult = (AsyncResult)iasycResult;
caller = (Algo1Delegate)asycResult.AsyncDelegate;//### PROBLEM!!!
// Retrieve the string Title that is passed in algodelegate.BeginInvoke().
string title = (string)iasycResult.AsyncState;
Image outputImage = caller.EndInvoke(iasycResult);
showImage(outputImage, title);
}
我认为您可以在上面的回调函数中看到问题。它仅适用于其他4个alog的Algo1,需要将其转换为Algo2Delegate,Algo3Delegate等。因为asycResult.AsyncDelegate的类型为object
。我怎么解决这个问题?我怎样才能让它适合其他人呢?
imageViewer窗口“无法响应”。我不明白为什么? ImageViewer
对象被初始化并显示在每个算法的同一个线程上。为什么它没有反应。
还有其他替代解决方案吗?
PS:我不能为所有算法声明一个delegateType,因为输入参数存在一些差异。
嗯,我的第一和第三个问题得到了足够的投入。我为每个算法使用了单独的回调。我的第二个问题仍未解决。我更改了ImageViewer()的构造函数只是为了检查它们是否在两个不同的线程上执行:
public ImageViewer(Image img, String title)
{
InitializeComponent();
if (pictureBox1.InvokeRequired) MessageBox.Show("You must Invoke()");
else MessageBox.Show("No need of Invoke()");
this.pictureBox1.Image = img;
this.Text = title + " : Image Viewer";
}
在每种情况下都说No need of Invoke()
。我不明白是什么问题。任何人都可以解决这个问题吗?我也没有得到任何执行。只是窗户变得反应迟钝。我检查算法是否造成任何麻烦。但不,他们不是。
答案 0 :(得分:1)
您应该使用您需要的常用方法替换具有一致层次结构的代理。
AsyncCallbackClass caller = (AlgoDelegate)asycResult.AsyncState;
Image img = caller.DoCallBack(iAsyncResult);
然后你有一个层次结构:
class AsyncCallback1 : AsyncCallbackClass
{
Image DoCallBack(IAsyncResult result)
{
// Call specific callback with specific parameters
}
}
class AsyncCallback2 : AsyncCallbackClass
{
Image DoCallBack(IAsyncResult result)
{
// Call specific callback with specific parameters
}
}
基本上,您将构建回调作为类的层次结构,以便main方法的“签名”相同(采用IAsyncResult的方法)并返回图像,但每个“委托”的方式(现在是一个完整的类)实现调用对于每个实现都是唯一的。
看看Replace Delegate with inheritance。
修改:msdn page。
如果控件的Handle是,则为true 在与...不同的线程上创建 调用线程(表示你 必须通过调用控件 一个调用方法);否则,错误。
我假设您正在ImageViewer中创建ImageBox,而ImageViewer正在回调中创建,因此,根据定义,ImageBox已由同一个线程创建,因此无需调用。
答案 1 :(得分:1)
我想不出一个干净的解决方案来解决你的问题。你必须编写这样的代码:
AsyncResult result = (AsyncResult)iresult;
if (result.AsyncDelegate is AsyncDelegate1) {
(result.AsyncDelegate as AsyncDelegate1).EndInvoke(iresult);
}
else if (result.AsyncDelegate is AsyncDelegate2) {
(result.AsyncDelegate as AsyncDelegate2).EndInvoke(iresult);
}
//etc...
ComputationResult answer = result.AsyncState as ComputationResult;
呸。你真的应该为每个委托类型都有一个单独的回调方法。泛型方法在这里无法帮助,约束不能是委托类型。 BeginInvoke方法调用中的lambda看起来不那么好:
var task1 = new AsyncDelegate1(Compute1);
var result1 = new ComputationResult("task1");
task1.BeginInvoke(42, result1,
new AsyncCallback((ia) => {
AsyncResult result = ia as AsyncResult;
(result.AsyncDelegate as AsyncDelegate1).EndInvoke(ia);
CommonCallback(result.AsyncState as ComputationResult);
}),
result1);
罗。我只使用一种委托类型来解决这个问题。 WaitCallback类型是合适的,虽然命名错误,但您应该编写一些小助手类来存储委托目标的参数,以便您可以通过WaitCallback.state参数传递它。
您的第二个问题是由于您在回调方法中创建ImageViewer实例而引发的。回调在线程池线程上执行,而不是在UI线程上执行。 InvokeRequired返回false,因为PictureBox控件是在线程池线程上创建的。但是,此线程池线程不适合显示UI组件,它不会引发消息循环。并且有错误的公寓状态。而且它很快就会终止。
当您使用在UI线程上创建的Control时,InvokeRequired将返回正确的值(true)。例如,您的主要启动表单。或Application.OpenForms [0]。使用InvokeRequired没什么意义,但是,您知道回调在错误的线程上执行的事实。只需直接使用BeginInvoke。调用的方法应该创建ImageViewer实例。
您正在重新发明BackgroundWorker类。它完全符合您的要求。但是要注意在正确的线程上触发RunWorkerCompleted事件的细节。你应该考虑一下。
答案 2 :(得分:0)
你可以将你的调用包装成lambda表达式,然后有一个启动委托的方法:
private void run(Action<Image,Image> delegate, Image inputImage)
{
delegate.BeginInvoke(inputImage, // all the callback stuff here );
}
然后用lambdas调用你的run方法:
run(image => algo1(image, otherVar, otherVar2));
run(image => algo2(image, otherVar, otherVar2, otherVar3, otherVar4));
等等
答案 3 :(得分:0)
几个月前我做了类似的事情,我使用的是ThreadPool:
http://msdn.microsoft.com/en-us/library/3dasc8as%28VS.80%29.aspx
http://www.switchonthecode.com/tutorials/csharp-tutorial-using-the-threadpool
它正在为您管理线程,并且非常容易用于不需要复杂多线程的任务。
答案 4 :(得分:0)
@ 1。您有五个代理,但您已为每个代理定义了一个通用的回调方法。因此,您将无法找到委托实际完成的内容。一种方法是为每个代表提供不同的回调方法。
@ 2您不应该从不同的线程更新UI而不是创建它。如果是,我们使用Control.Invoke确保调用被封送到UI线程。
MethodInvoker updateImageViewer = delegate
{
ImageViewer imageviewer = new ImageViewer(image, title);
imageviewer.Show();
};
if (this.pictureBox1.InvokeRequired)
this.pictureBox1.Invoke(updateImageViewer);
else
updateImageViewer();