CLR via C#:IO限制的异步操作

基础知识:如下所示:
1.NTFS文件系统设备驱动程序总是以同步方式执行一些操作,不管具体如何打开文件。
2.使用FileOptions.Asynchronous标志来表明异步执行IO操作,此时调用XxxAsync函数可以获得最佳性能。
3.不使用FileOptions.Asynchronous标志来表明同步执行IO操作,此时调用Xxx函数可以获得最佳性能。

执行同步IO操作:流程如下图所示:在这里插入图片描述
执行异步IO操作:流程如下图所示:在这里插入图片描述
async操作符:具有以下特性:
1.使用async操作符来标记的函数就是异步函数。
2.异步函数的返回类型只能是void或者Task或者Task<T>。
3.异步函数的参数不能使用out或者ref操作符。
4.当异步函数返回类型为Task<T>时,异步函数体就返回T类型变量;否则就什么都不返回。
5.不能将应用程序的Main函数转变成异步函数。
6.不能将类型的构造函数,属性访问器函数以及事件访问器函数转变成异步函数。
7.编译器执行async操作符的流程如下所示:
1>.生成一个派生自IAsyncStateMachine类的状态机类。
2>.创建状态机对象,并设置该对象的异步函数生成器字段,初始状态字段以及实参字段。
3>.调用状态机对象的Start函数来启动状态机。
4>.当异步函数返回类型为void时就什么都不返回。
5>.当异步函数返回类型为Task时就返回Task对象。该对象会在状态机对象执行完毕时自动完成。
6>.当异步函数返回类型为Task<T>时就返回Task<T>对象。该对象会在状态机对象执行完毕时自动完成,并将异步函数的返回结果填充到Result字段中。

await操作符:具有以下特性:
1.await操作符只能在异步函数内部使用。
2.不能在catch,finally以及unsafe块中使用await操作符。
3.不能在await操作符之前获得一个支持线程所有权或递归的锁,并在await操作符之后释放它。
4.在查询表达式中,await操作符只能在初始from子句的第一个集合表达式中使用;或者在join子句的集合表达式中使用。
5.编译器执行await操作符的流程如下所示:
1>.调用操作对象的GetAwaiter函数来获取等待者对象。
2>.操作以同步方式执行时,流程如下所示:
1>>.调用等待者对象的IsCompleted属性。该属性将返回true。
2>>.调用等待者对象的GetResult函数。该函数要么抛出异常来表示操作失败;要么返回结果来表示操作成功,然后调用线程从await操作符处继续向下执行。
3>.操作以异步方式执行时,流程如下所示:
1>>.调用等待者对象的IsCompleted属性。该属性将返回false。
2>>.调用等待者对象的OnCompleted函数。该函数接收一个委托。该委托执行流程如下所示:
1>>>.调用线程进入到await操作符位置处。
2>>>.调用等待者对象的GetResult函数。该函数要么抛出异常来表示操作失败;要么返回结果来表示操作成功,然后调用线程从await操作符处继续向下执行。
3>>.调用线程从await操作符处直接返回。
4>>.等待者对象完成时,就调用委托。
6.可以使用TaskFactory.FromAsync函数将基于BeginXXX函数和EndXXX函数的异步编程模型转换成基于Task的异步编程模型。但是尽量不要使用IAsyncResult类型作为参数的TaskFactory.FromAsync函数,因为它们性能低效。参考代码如下所示:

private static async void StartServer()
{
	while (true)
	{
		var pipe = new NamedPipeServerStream(c_pipeName, PipeDirection.InOut, -1, PipeTransmissionMode.Message, PipeOptions.Asynchronous | PipeOptions.WithThrough);

		// 异步的接受客户端连接
		// 注意:NamedPipeServerStream使用旧的异步编程模型
		// 我用TaskFactory的FromAsync函数将旧的异步编程模型转换成Task异步编程模型
		await Task.Factory.FromAsync(pipe.BeginWaitForConnection, pipe.EndWaitForConnection, null);
		// 开始为客户端服务,因为是异步的,所以可以立即返回
		ServiceClientRequestAsync(pipe);
	}
}

7.可以使用TaskCompletionSource类型将基于事件的异步编程模型转换成基于Task的异步编程模型。参考代码如下所示:

private static async Task<String> AwaitWebClient(Uri uri)
{
	// System.Net.WebClient类支持基于事件的异步编程模型
	var wc = new System.Net.WebClient();
	// 创建TaskCompletionSource及其基础Task对象
	var tcs = new TaskCompletionSource<String>();
	// 字符串下载完毕后,WebClient对象引发DownloadStringCompleted事件
	// 从而完成TaskCompletionSource对象
	wc.DownloadStringCompleted += (s, e) => {
		if (e.Cancelled) tcs.SetCanceled();
		else if (e.Error != null) tcs.SetException(e.Error);
		else tcs.SetResult(e.Result);
	};
	// 启动异步操作
	wc.DownloadStringAsync(uri);
	// 现在可以等待TaskCompletionSource的Task对象
	String result = await tcs.Task;
	return result;
}

8.向Task.ConfigureAwait函数传递true相当于根本没有调用该函数;否则await操作符就不查询调用线程的SynchronizationContext对象,当线程池线程结束Task对象时会直接完成它,await操作符后面的代码通过线程池线程执行。

IO请求优先级:Windows允许线程在发出IO请求时指定优先级,但是FCL还没有包含这个功能。此时可以使用P/Invoke本机Win32函数来实现这个功能,参考代码如下所示:

internal static class ThreadIO
{
    public static BackgroundProcessingDisposer BeginBackgroundProcessing(Boolean process = false)
    {
        ChangeBackgroundProcessing(process, true);
        return new BackgroundProcessingDisposer(process);
    }

    public static void EndBackgroundProcessing(Boolean process = false)
    {
        ChangeBackgroundProcessing(process, false);
    }

    private static void ChangeBackgroundProcessing(Boolean process, Boolean start)
    {
        Boolean ok = false;
        if (process)
        {
            ok = SetPriorityClass(GetCurrentWin32ProcessHandle(), start ? ProcessBackgroundMode.Start : ProcessBackgroundMode.End);
        }
        else
        {
            ok = SetThreadPriority(GetCurrentWin32ThreadHandle(), start ? ThreadBackgroundMode.Start : ThreadBackgroundMode.End);
        }

        if (!ok)
        {
            throw new Win32Exception();
        }
    }
    
    // 这个结构使C#的using语句能终止后台处理模式
    public struct BackgroundProcessingDisposer
    {
        private readonly Boolean m_process;

        public BackgroundProcessingDisposer(Boolean process)
        {
            m_process = process;
        }

        public void Dispose()
        {
            EndBackgroundProcessing(m_process);
        }
    }
    
    // 参见WIN32的THREAD_MODE_BACKGROUND_BEGIN和THREAD_MODE_BACKGROUND_END
    private enum ThreadBackgroundMode
    {
        Start = 0x10000,
        End = 0x20000
    }
    
    // 参见WIN32的PROCESS_MODE_BACKGROUND_BEGIN和PROCESS_MODE_BACKGROUND_END
    private enum ProcessBackgroundMode
    {
        Start = 0x100000,
        End = 0x200000
    }

    [DllImport("Kernel32", EntryPoint = "GetCurrentProcess", ExactSpelling = true)]
    private static extern SafeWaitHandle GetCurrentWin32ProcessHandle();
    
    [DllImport("Kernel32", ExactSpelling = true, SetLastError = true)]
    [return:MarshalAs(UnmanagedType.Bool)]
    private static extern Boolean SetPriorityClass(SafeWaitHandle hProcess, ProcessBackgroundMode mode);
    
    [DllImport("Kernel32", EntryPoint = "GetCurrentThread", ExactSpelling = true)]
    private static extern SafeWaitHandle GetCurrentWin32ThreadHandle();
    
    [DllImport("Kernel32", ExactSpelling = true, SetLastError = true)]
    [return:MarshalAs(UnmanagedType.Bool)]
    private static extern Boolean SetThreadPriority(SafeWaitHandle hThread, ThreadBackgroundMode mode);
    
    [DllImport("Kernel32", SetLastError = true, EntryPoint = "CancelSynchronousIo")]
    [return:MarshalAs(UnmanagedType.Bool)]
    private static extern Boolean CancelSynchronousIO(SafeWaitHandle hThread);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值