读书人

异步调用System.IO.FileStream的Begin

发布时间: 2013-02-20 12:23:22 作者: rapoo

求助:异步调用System.IO.FileStream的BeginRead的返回值IAsyncResult的IsCompleted属性的问题
本帖最后由 yayiba2020 于 2013-02-13 12:00:49 编辑 我最近找到了这样的一段代码,他想演示如何解决异步操作的线程同步问题。
具体操作是这样的:主线程异步调用后不断地轮询判断异步调用返回的IAsyncResult的IsCompleted(标示是否异步调用完成)属性,如果IsCompleted为真就不再轮询,进行后续的操作。
我在运行代码时发现了问题:异步操作没有完成的时候,IsCompleted的值为true了。然后我又从MSDN上面复制一段代码调试,发现MSDN的运行结果正常,只有在异步操作完成后IsCompleted的值才由false变为true。
我个人认为这两段代码的区别就是MSDN异步调用自定义委托的BeginInvoke,另外一段代码是异步调用System.IO.FileStream类的BeginRead
//这是MSDN的代码:


using System;
using System.Threading;


namespace ConsoleApplication1
{

public class AsyncDemo
{
// The method to be executed asynchronously.
public string TestMethod(int callDuration, out int threadId)
{
Console.WriteLine("Test method begins.");
Thread.Sleep(callDuration);
threadId = Thread.CurrentThread.ManagedThreadId;
return String.Format("My call time was {0}.", callDuration.ToString());
}
}
// The delegate must have the same signature as the method
// it will call asynchronously.
public delegate string AsyncMethodCaller(int callDuration, out int threadId);


class Program
{
static void Main(string[] args)
{
// The asynchronous method puts the thread id here.
int threadId;

// Create an instance of the test class.
AsyncDemo ad = new AsyncDemo();

// Create the delegate.
AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);

// Initiate the asychronous call.
IAsyncResult result = caller.BeginInvoke(10000,
out threadId, null, null);

// Poll while simulating work.
while (result.IsCompleted == false)
{
Thread.Sleep(250);


Console.Write(".");
}

// Call EndInvoke to retrieve the results.
string returnValue = caller.EndInvoke(out threadId, result);

Console.WriteLine("\nThe call executed on thread {0}, with return value \"{1}\".",
threadId, returnValue);
Console.Read();

}
}
}


//IsCompleted判断有问题的代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Threading;

namespace chapter4_thread_5
{
class Program
{
static void Main(string[] args)
{
APM_4_1();
}

/// <summary>
/// 采用主线程轮询判断异步处理是否完成的方式解决异步编程的同步问题
/// </summary>
private static void APM_4_1()
{
Console.WriteLine("主程序中!");
Console.WriteLine("线程的 Id: {0}", Thread.CurrentThread.ManagedThreadId);
Console.WriteLine();

string path = "../../demo.log";
byte[] buffer = new byte[4096];

System.IO.FileStream fs = new System.IO.FileStream(path, System.IO.FileMode.Open);
IAsyncResult result = fs.BeginRead(buffer, 0, 4096,
delegate(IAsyncResult ar)
{
Thread.Sleep(6000);
Console.WriteLine("第三步操作中。");
Console.WriteLine("线程的 Id: {0}", Thread.CurrentThread.ManagedThreadId);
int length = fs.EndRead(ar);
string message = System.Text.Encoding.UTF8.GetString(buffer, 0, length);


Console.WriteLine(message);
Console.WriteLine();
},
null);

Console.WriteLine("第一步操作已经完成。现在主程序继续进行!");
Console.WriteLine("线程的 Id: {0}", Thread.CurrentThread.ManagedThreadId);
Console.WriteLine();

// 等待第二个线程完成
while (!result.IsCompleted)
{
Thread.Sleep(10);
}

Console.WriteLine("注意,此时第三步操作已经完成,回车后结束程序。");
Console.WriteLine();

Console.ReadLine();
}


}
}

APM
[解决办法]
你在异步回调中Thread.Sleep(6000);是毫无意义的,那个result.IsCompleted指示了异步操作的完成,并不是异步回调的完成,当异步操作完成后,就会更改IsCompleted的属性值为true,并执行异步回调函数。
既然你用到了异步回调函数,根本不需要在主线程中轮询异步状态,直接把后续操作写在异步回调中即可。
[解决办法]
引用:
我最近找到了这样的一段代码,他想演示如何解决异步操作的线程同步问题。
具体操作是这样的:主线程异步调用后不断地轮询判断异步调用返回的IAsyncResult的IsCompleted(标示是否异步调用完成)属性,如果IsCompleted为真就不再轮询,进行后续的操作。

这种垃圾代码一定要尽一切可能去避免。

主线程异步调用某个操作,它只要把回调方法作为参数注册给调用操作,然后就结束了。

主线程一旦阻塞在那里去轮询你的标志,主线程还能干正经事吗?此时你的程序就看起来“卡死了”。

因此异步调用操作,就不应该出现什么“循环”代码。这种垃圾代码一定要尽可能去避免。

读书人网 >C#

热点推荐