接上文 多线程编程学习笔记——使用并发集合(一)

接上文 多线程编程学习笔记——使用并发集合(二)

接上文 多线程编程学习笔记——使用并发集合(三)

假设以下场景,如果在客户端运行程序,最的事情之一是有一个响应的用户界面。这意味着无论应用程序发生什么,所有的用户界面元素都要保持 快速运行,用户能够从应用程序得到快速响应。达到这一点并不容易!如果你尝试在Windows系统中打开记事本并加载一个有几兆大小的文档,应用程序窗口将交结一段的时间,因为整个文件要先从硬盘中加载,然后程序才能开始处理用户输入。

这是一个非常重要的问题,在这种情况下,唯一方案是无论如何都要避免阻塞UI纯种。这反过来意味着为了防止阻塞UI线程,每个与UI有关的API必须只被允许异步调用 。这是Windows操作系统重新升级API的关键原因 ,其几乎把每个方法替换为异步方式。但是应用程序使用多线程来达到此目的会影响性能吗?当然会。然而考虑到只有一个用户,那么这是划算的。如果应用程序可以使用电脑的所有能力从而变得更加高效,而且这种能力 只为运行程序的唯一用户服务,这是好事。

接下来看看第二种情况。如果程序运行在服务器端,则是完全不同的情形。可伸缩性是最高优先级,这意味着单个 用户消耗越少的资源越好。如果为每个用户创建多个线程,则可伸缩性并不好。以高效的方式来平衡应用程序资源的消耗是个非常复杂的问题。例如,在ASP.NET中,我们使用工作线程池来服务客户端请求。这个池的工作线程是有限的,所以不得不最小化每个工作线程的使用时间以便达到高伸缩性。这意味着需要把工作线程越快越好地放回到池中,从而可以服务下一个请求。如果我们启动了一个需要计算的异步操作,则整个工作流程会很低效。首先从线程池中取出一个工作 线程用以服务客户端请求。然后取出另一个工作线程并开始处理异步操作。现在有两个工作线程都在处理请求,如果第一个线程能做些有用的事则非常好。可惜,通常情况下,我们简单等待异步 操作完成,但是我们却消费了两个工作 线程,而不是一个。在这个场景中,异步 比同步执行实际上更糟糕!我们不需要使用所有CPU核心,因为我们已经在服务很多客户端,它们已经使用了CPU的所有计算能力。我们无须保持第一个线程响应,因为这没有用户界面。那么为什么我们应该在服务端使用异步呢?

答案是只有异步输入/输出操作才应用使用异步。目前,现代计算机通过有一个磁盘驱动器来存储文件,一块网卡来通过网络发送与接收数据。所有这些设备都有自己的芯片,以非常底层的方式来管理输入/输出操作并发信号 给操作系统。这种执行I/O任务的方式被称为I/O线程。

在ASP.NET中,一旦有一个异步的I/O操作在工作线程开始时,它会被立即返回到线程池中。当这个操作继续运行时,这个线程可以服务其他的客户端。最终,当操作发出信号完成时,ASP.NET基础设施从线程池中获取一个空闲的工作线程,然后会完成这个操作。

一、   异步使用文件

本救命学习如何使用异步的方式读写一个文件。

1.示例代码如下。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace ThreadIODemo
{
class Program
{ static void Main(string[] args)
{
Console.WriteLine("--开始 使用 异步 I/O 线程 -- ");
var t = ReadWriteAsyncIO();
t.GetAwaiter().GetResult();
Console.Read();
} const int BUFFER_SIZE = ; async static Task ReadWriteAsyncIO()
{ using (var fs = new FileStream("test1.txt", FileMode.Create, FileAccess.ReadWrite, FileShare.None, BUFFER_SIZE))
{ Console.WriteLine("1. 使用 I/O 线程 是否异步:{0}",fs.IsAsync);
byte[] buffer = Encoding.UTF8.GetBytes(CreateFileContent()); var writeTask = Task.Factory.FromAsync(fs.BeginWrite, fs.EndWrite, buffer, , buffer.Length, null);
await writeTask; } using (var fs = new FileStream("test2.txt", FileMode.Create, FileAccess.ReadWrite, FileShare.None,
BUFFER_SIZE, FileOptions.Asynchronous))
{ Console.WriteLine("2. 使用 I/O 线程 是否异步:{0}",fs.IsAsync);
byte[] buffer = Encoding.UTF8.GetBytes(CreateFileContent());
var writeTask = Task.Factory.FromAsync(fs.BeginWrite, fs.EndWrite, buffer, , buffer.Length, null);
await writeTask;
} using (var fs = File.Create("test3.txt", BUFFER_SIZE, FileOptions.Asynchronous))
{ using (var sw = new StreamWriter(fs))
{
Console.WriteLine("3. 使用 I/O 线程 是否异步:{0}",fs.IsAsync);
await sw.WriteAsync(CreateFileContent());
}
} using (var sw = new StreamWriter("test4.txt", true))
{ Console.WriteLine("4. 使用 I/O 线程 是否异步:{0}",((FileStream)sw.BaseStream).IsAsync);
await sw.WriteAsync(CreateFileContent());
} System.Threading.Thread.Sleep();
Console.WriteLine("开始异步读取文件"); Task<long>[] readTasks = new Task<long>[];
for (int i = ; i < ; i++)
{ readTasks[i] = SumFileContent(string.Format("test{0}.txt",i + )); } long[] sums = await Task.WhenAll(readTasks);
Console.WriteLine("所有文件中的和值:{0}", sums.Sum()); Console.WriteLine("开始删除文件");
Task[] delTasks = new Task[];
for (int i = ; i < ; i++)
{ string filename = string.Format("test{0}.txt",i + );
delTasks[i] = SimulateAsynchronousDelete(filename); } await Task.WhenAll(delTasks);
Console.WriteLine("删除文件结束");
} static string CreateFileContent()
{
var sb = new StringBuilder();
for (int i = ; i < ; i++)
{
sb.AppendFormat("{0}", new Random(i).Next(, ));
sb.AppendLine();
}
return sb.ToString();
} async static Task<long> SumFileContent(string filename)
{
using (var fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.None, BUFFER_SIZE, FileOptions.Asynchronous))
using (var sr = new StreamReader(fs))
{ long sum = ;
while (sr.Peek() > -)
{
string line = await sr.ReadLineAsync();
sum += long.Parse(line);
}
return sum;
}
} static Task SimulateAsynchronousDelete(string filename)
{
return Task.Run(() => File.Delete(filename));
} }
}

2.程序运行结果,如下图。

当程序运行时,我们以不同的方式创建了4个文件,并写入一些随机数据。

在第一个例子中,使用的是FileStream类以及其方式,将异步编程模式API转换成任务。

在第二个例子中,使用的是FileStream类以及其方式,不过在构造的时候提供了FileStream.Asynchronous参数 。

在第三个例子使用了一些简化的API,比如File.Create方法和StreamWrite类。它也使用I/O线程,我们可以使用Stream.iSaSYNC属性来检查。

在第四个例子说明了过分简化 也不好。这里我们借助异步委托调用来模拟异步I/O,其实并没有使用异步I/O。

然后并行地异步地从所有文件中读取数据,统计每个文件内容,然后求总和。

最后,删除所有文件。

多线程编程学习笔记——使用异步IO的更多相关文章

  1. 多线程编程学习笔记——使用异步IO(一)

    接上文 多线程编程学习笔记——使用并发集合(一) 接上文 多线程编程学习笔记——使用并发集合(二) 接上文 多线程编程学习笔记——使用并发集合(三) 假设以下场景,如果在客户端运行程序,最的事情之一是 ...

  2. 多线程编程学习笔记——异步调用WCF服务

    接上文 多线程编程学习笔记——使用异步IO 接上文 多线程编程学习笔记——编写一个异步的HTTP服务器和客户端 接上文 多线程编程学习笔记——异步操作数据库 本示例描述了如何创建一个WCF服务,并宿主 ...

  3. 多线程编程学习笔记——编写一个异步的HTTP服务器和客户端

    接上文 多线程编程学习笔记——使用异步IO 二.   编写一个异步的HTTP服务器和客户端 本节展示了如何编写一个简单的异步HTTP服务器. 1.程序代码如下. using System; using ...

  4. 多线程编程学习笔记——async和await(一)

    接上文 多线程编程学习笔记——任务并行库(一) 接上文 多线程编程学习笔记——任务并行库(二) 接上文 多线程编程学习笔记——任务并行库(三) 接上文 多线程编程学习笔记——任务并行库(四) 通过前面 ...

  5. 多线程编程学习笔记——async和await(二)

    接上文 多线程编程学习笔记——async和await(一) 三.   对连续的异步任务使用await操作符 本示例学习如何阅读有多个await方法方法时,程序的实际流程是怎么样的,理解await的异步 ...

  6. 多线程编程学习笔记——async和await(三)

    接上文 多线程编程学习笔记——async和await(一) 接上文 多线程编程学习笔记——async和await(二) 五.   处理异步操作中的异常 本示例学习如何在异步函数中处理异常,学习如何对多 ...

  7. [Java123] JDBC and Multi-Threading 多线程编程学习笔记

    项目实际需求:DB交互使用多线程实现 多线程编程基础:1.5  :( (假设总分10) 计划一个半月从头学习梳理Java多线程编程基础以及Oracle数据库交互相关的多线程实现 学习如何通过代码去验证 ...

  8. Java多线程编程(学习笔记)

    一.说明 周末抽空重新学习了下多线程,为了方便以后查阅,写下学习笔记. 有效利用多线程的关键是理解程序是并发执行而不是串行执行的.例如:程序中有两个子系统需要并发执行,这时候需要利用多线程编程. 通过 ...

  9. Python3 多线程编程 - 学习笔记

    线程 什么是线程 特点 线程与进程的关系 Python3中的多线程 全局解释器锁(GIL) GIL是啥? GIL对Python程序有啥影响? 改善GIL产生的问题 Python3关于多线程的模块 多线 ...

随机推荐

  1. Leetcode——Two Sum(easy)

    题目:Given nums = [2, 7, 11, 15], target = 9, Because nums[0] + nums[1] = 2 + 7 = 9, return [0, 1] 代码: ...

  2. Player2.0

    由于上次做的播放器实在是有点丑,加之这期间学习了许多关于页面排版的东西,这次做第二次作业就重新来了一遍“换汤不换药”的操作.其实刚开始看到的作业要求的时候,有点傻眼猫咪,但是做到现在这个程度,本人也是 ...

  3. C++标准库第二版笔记 2.1

    C++标准库第二版笔记 2.1 1 Range-Based for 循环 for ( decl : coll ) { statements; } // collaborate 类似C# foreach ...

  4. springsecurity 源码解读之 AnonymousAuthenticationFilter

    我们知道springsecutity 是通过一系列的 过滤器实现的,我们可以看看这系列的过滤器到底长成什么样子呢? 一堆过滤器,这个过滤器的设计设计上是 责任链设计模式. 这里我们可以看到有一个 An ...

  5. go mysql insert变量到数据库

    result, err1 := db.Exec("insert ignore into dish(name,calorie,confidence) values('"+str1+& ...

  6. Appium系列文章(1)获取appPackage和appActivity

    appPackage和appActivity 进行appium自动化测试非常重要的两个参数,我们所测试的APP不同,这两个参数肯定也是不一样的.那如何快速的获取这APP的这两个参数呢? 通过cmd命令 ...

  7. 【转】mysqldump与innobackupex知多少

    作者:罗小波 [目录] 1. 先看mysqldump 1.1 mysqldump备份过程解读 1.2 mysqldump备份过程中的关键步骤 1.2.1 FLUSH TABLES和FLUSH TABL ...

  8. redis_哈希对象

    redis哈希对象的底层编码有两种:ziplist.hashtable ziplist编码 当一个哈希键只包含少量kv对.且key和value都是小整数值.短字符串时,redis会使用压缩列表来做 z ...

  9. c++中二级指针的使用场景

    二级指针的使用场景如下: 1.主要用来为指针变量分配内存空间: void GetMemory(char **p) { *p = ]; } 函数调用方式: char *str = NULL; GetMe ...

  10. git忽视修改的文件

    对于tracked文件来说,使用.gitignore是不行的.但是git提供了新的方法. 使用命令 git update-index --assume-unchanged <files>, ...