我正在学习线程。我的意思是将一些值传递给计算方法,如果结果不会在20毫秒内返回,我将报告“操作超时”。基于我的不足之处,我已将代码实现为如下:
public delegate long CalcHandler(int a, int b, int c);
public static void ReportTimeout()
{
CalcHandler doCalculation = Calculate;
IAsyncResult asyncResult =
doCalculation.BeginInvoke(10,20,30, null, null);
if (!asyncResult.AsyncWaitHandle.WaitOne(20, false))
{
Console.WriteLine("..Operation Timeout...");
}
else
{
// Obtain the completion data for the asynchronous method.
long val;
try
{
val= doCalculation.EndInvoke(asyncResult);
Console.WriteLine("Calculated Value={0}", val);
}
catch
{
// Catch the exception
}
}
}
public static long Calculate(int a,int b,int c)
{
int m;
//for testing timeout,sleep is introduced here.
Thread.Sleep(200);
m = a * b * c;
return m;
}
问题:
(1)报告超时是否正确?
(2)如果时间结束,我不会调用EndInvoke()。在这种情况下,调用EndInvoke()是必需的吗?
(3)我听说
“即使你不想处理异步方法的返回值,你也应该调用EndInvoke;否则,每次使用BeginInvoke” <启动异步调用时,你都有可能泄漏内存/ p>
与记忆相关的风险是什么?你举个例子吗?
答案 0 :(得分:2)
<强>答案强>
(1)通常你只是抛出System.TimeoutException
(2)需要EndInvoke()的原因是,如果返回值(或抛出的异常)具有内存分配,GC可能无法快速清理它。我没有看到这是一个大问题的案例......但那只是我。
如果您真的担心它,您可以调用ThreadPool.QueueUserWorkItem,它主要是 上述异步调用将执行的操作。 (注意:在WinForms或其他依赖于线程的上下文中可能存在问题)。无论如何,您可以按如下方式运行自己的工作项:
public static long Calc(CalcHandler fn, int a, int b, int c)
{
return Run<long>(TimeSpan.FromSeconds(20), delegate { return fn(a, b, c); });
}
public static T Run<T>(TimeSpan timeout, Func<T> operation)
{
Exception error = null;
T result = default(T);
ManualResetEvent mre = new ManualResetEvent(false);
System.Threading.ThreadPool.QueueUserWorkItem(
delegate(object ignore)
{
try { result = operation(); }
catch (Exception e) { error = e; }
finally { mre.Set(); }
}
);
if (!mre.WaitOne(timeout, true))
throw new TimeoutException();
if (error != null)
throw new TargetInvocationException(error);
return result;
}
答案 1 :(得分:1)
从超时值开始,是的,这将按照您的预期设置超时。
至于记忆风险。存在这样的风险:如果您不调用EndInvoke,仍然会有对该委托的引用,并且在应用程序退出之前,所使用的任何资源都可能不会被垃圾收集。我发现的确切实现并非100%记录,但可以使用像ANTS Profiler这样的工具进行确认。
这是一个有用的discussion。