在看微软的ASP.NET - 将 ASP.NET 用作高性能文件下载器 示例里面用到了IO 线程,以前打算自己撸的,这里贴出来 已标记一下:

////////////////////////////////////////////////////
// A delegate's BeginInvoke runs on a "worker thread" inside the CLR ThreadPool.
// This allows you to run a delegate on a "completion port thread" inside the CLR ThreadPool
// instead of a "worker thread" by calling IOBeginInvoke instead of BeginInvoke.
// In a nut shell, this mechanism basically skips the actual system I/O and simply posts a
// completion delegate to the completion port queue so the completion delegate will be
// executed by a "completion port thread" that is working the completion port queue.
//
// Example:
// delegate.BeginInvoke => executes delegate on "worker thread".
// delegate.IOBeginInvoke => executes delegate on "completion port thread".
//
//
// Extremely simplified explanation:
//
// CLR ThreadPool is made up of two pools of threads: "worker threads" and "completion port threads".
//
// Basically you can either queue a user work item which runs the delegate on a "worker thread",
// or queue a native overlapped item which runs the completion delegate on a "completion port thread".
// ThreadPool.QueueUserWorkItem (and delegate.BeginInvoke) => executes delegate on "worker thread".
// ThreadPool.UnsafeQueueNativeOverlapped => executes completion delegate on "completion port thread".
//
// (CLR ThreadPool)
// / \
// [worker threads] [completion port threads]
//
// o o oo o oo _____________post to queue using ThreadPool.UnsafeQueueNativeOverlapped
// o o oo o o | (i.e. PostQueuedCompletionStatus)
// o o o o o v
// oo o oo o o | |
// o oo oo o | | <----------completion port queue (one per process)
// |__|
// ^ oo o
// | o o oo <---------completion port threads (work the completion port queue)
// | (cpu)(cpu)(cpu)(cpu) (i.e. GetQueuedCompletionStatus in loop)
// |
// | ^
// | |
// | |
// | |
// | |
// | Each individual completion delegate is given to the completion port queue to execute,
// | and the "completion port threads" working the completion port queue execute the delegate.
// | (This has much less risk of thread explosion because each delegate just gets posted to the
// | completion port queue, instead of being given to its own individual thread. Basically
// | the queue grows, not the threads.)
// |
// | The completion delegate is supplied in the overlapped structure that is posted to the
// | completion port queue.
// |
// | Posting to queue (PostQueuedCompletionStatus) => done by ThreadPool.UnsafeQueueNativeOverlapped.
// | Working queue (GetQueuedCompletionStatus in loop) => done by "completion port thread" working queue.
// |
// |
// Each individual delegate is given to its own individual "worker thread" to execute,
// and the OS schedules the "worker thread" to run on a cpu.
// (This has the risk of thread explosion because each new delegate executes on its own
// individual "worker thread". If the number of threads grows to large the entire OS can
// grind to a hault, with all the memory used and the cpu's spending all their time
// just trying to schedule the threads to run but not actually doing any work.)
//
////////////////////////////////////////////////////
public static class IOThread
{
public delegate void ProcessRequestDelegate(HttpContext context); public class IOAsyncResult : IAsyncResult
{
public object AsyncState { get; set; }
public WaitHandle AsyncWaitHandle { get; set; }
public Delegate AsyncDelegate { get; set; }
public bool CompletedSynchronously { get; set; }
public bool IsCompleted { get; set; }
public Exception Exception { get; set; }
} unsafe public static IAsyncResult IOBeginInvoke(this ProcessRequestDelegate value, HttpContext context, AsyncCallback callback, object data)
{
ManualResetEvent evt = new ManualResetEvent(false); IOAsyncResult ar = new IOAsyncResult();
ar.AsyncState = new object[] { context, callback, data };
ar.AsyncDelegate = value;
ar.AsyncWaitHandle = evt; Overlapped o = new Overlapped(, , IntPtr.Zero, ar);
NativeOverlapped* no = o.Pack(new IOCompletionCallback(ProcessRequestCompletionCallback), data);
ThreadPool.UnsafeQueueNativeOverlapped(no); return ar;
} unsafe private static void ProcessRequestCompletionCallback(uint errorCode, uint numBytes, NativeOverlapped* no)
{
try
{
Overlapped o = Overlapped.Unpack(no); ProcessRequestDelegate d = (ProcessRequestDelegate)((IOAsyncResult)o.AsyncResult).AsyncDelegate; object[] state = (object[])o.AsyncResult.AsyncState;
HttpContext context = (HttpContext)state[];
AsyncCallback callback = (AsyncCallback)state[];
object data = state[]; try
{
d(context);
}
catch(Exception ex)
{
((IOAsyncResult)o.AsyncResult).Exception = ex;
} ((IOAsyncResult)o.AsyncResult).IsCompleted = true;
((ManualResetEvent)o.AsyncResult.AsyncWaitHandle).Set(); if (callback != null)
callback(o.AsyncResult);
}
finally
{
Overlapped.Free(no);
}
} unsafe public static void IOEndInvoke(this ProcessRequestDelegate value, IAsyncResult result)
{
IOAsyncResult ar = (IOAsyncResult)result;
ar.AsyncWaitHandle.WaitOne(Timeout.Infinite);
if (ar.Exception != null)
throw ar.Exception;
}
}

使用示例:

  ////////////////////////////////////////////////////
// Example URLs to call download handler and download a file:
//
// https://localhost/DownloadPortal/Download?file=file.txt
// https://localhost/DownloadPortal/Download?file=file.txt&chunksize=1000000
// https://localhost/DownloadPortal/Download?file=customer1/file.txt
// https://localhost/DownloadPortal/Download?file=customer1/file.txt&chunksize=1000000
//
// Important!!! Must replace 'localhost' in URL with actual machine name or SSL certificate will fail.
//
// Can either include the 'Range' HTTP request header in the HTTP web request OR
// supply the 'chunksize' parameter in the URL. If include the 'Range' HTTP request header,
// it will transmit back that portion of the file identified by the 'Range'. If supply the
// 'chunksize' parameter, it will transmit back the entire file in separate pieces the size of
// 'chunksize'. If supply both, 'Range' will be used. If supply neither, it will transmit back
// the entire file in one piece.
//
// Web.config on IIS 7.0:
// <system.webServer>
// <handlers>
// <add name="Download" verb="*" path="Download" type="DownloadHandlers.DownloadHandler" />
// </handlers>
// </system.webServer>
//
// Put DownloadHandler.dll and IOThreads.dll in the /bin directory of the virtual directory on IIS.
//
// Put files to be downloaded into virtual directory or sub directory under virtual directory.
//
// To debug:
// Debug \ Attach to Process \ w3wp.exe
//
// Note: Make sure the IIS virtual directory is using an AppPool that is using .NET Framework 4.0.
// Define a new AppPool using .NET Framework 4.0, if no other AppPool exists that is using .NET Framework 4.0.
//////////////////////////////////////////////////// public class DownloadHandler : IHttpAsyncHandler
{
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
{
//Offloads to "completion port threads" inside CLR ThreadPool
//instead of "worker threads" inside CLR ThreadPool, so that our
//work doesn't use up the "worker threads" that are needed for the
//IIS server's request processing. (see comment section at top of
//IOThread class for more details)
IOThread.ProcessRequestDelegate d = ProcessRequest;
return d.IOBeginInvoke(context, cb, extraData);
} public void EndProcessRequest(IAsyncResult result)
{
IOThread.IOAsyncResult ar = (IOThread.IOAsyncResult)result;
IOThread.ProcessRequestDelegate d = (IOThread.ProcessRequestDelegate)ar.AsyncDelegate;
d.IOEndInvoke(result);
} public void ProcessRequest(HttpContext context)
{
try
{
string file = context.Request.QueryString["File"];
if (string.IsNullOrEmpty(file))
throw new Exception("Must specify file in query string. (Example: Download?File=your file)"); string fileName = Path.GetFileName(file);
if (string.IsNullOrEmpty(fileName))
throw new Exception("File name '" + fileName + "' is not valid."); long chunkSize = ;
if (!string.IsNullOrEmpty(context.Request.QueryString["ChunkSize"]))
if (context.Request.QueryString["ChunkSize"].Trim().Length > )
chunkSize = long.Parse(context.Request.QueryString["ChunkSize"]); FileInfo fi = new FileInfo(context.Server.MapPath(file));
long fileLength = fi.Length;
if (chunkSize > && chunkSize > fileLength)
throw new Exception("ChunkSize is greater than file length."); context.Response.ClearHeaders();
context.Response.ClearContent();
context.Response.Clear(); //request is just checking file length
if (context.Request.HttpMethod == "HEAD")
{
context.Response.AddHeader("content-length", fileLength.ToString());
context.Response.AddHeader("accept-ranges", "bytes");
context.Response.Flush();
return;
} //file save
context.Response.ContentEncoding = Encoding.UTF8;
context.Response.AddHeader("content-disposition", "attachment;filename=\"" + fileName + "\"");
//context.Response.AddHeader("content-disposition", "attachment;filename=\"" + HttpUtility.UrlEncode(fileName) + "\"");
context.Response.ContentType = "application/octet-stream";
context.Response.AddHeader("cache-control", "no-store, no-cache");
context.Response.ExpiresAbsolute = DateTime.Now.Subtract(new TimeSpan(, , , ));
context.Response.Expires = -;
context.Response.AddHeader("Connection", "Keep-Alive");
context.Response.AddHeader("accept-ranges", "bytes"); //request is for byte range
if (!string.IsNullOrEmpty(context.Request.Headers["Range"]) && context.Request.Headers["Range"].Trim().Length > )
{
string range = context.Request.Headers["Range"];
range = range.Replace(" ", "");
string[] parts = range.Split(new char[] { '=', '-' });
long contentLength = long.Parse(parts[]) - long.Parse(parts[]);
context.Response.AddHeader("content-length", contentLength.ToString());
string contentRange = string.Format("bytes {0}-{1}/{2}", parts[], parts[], fileLength.ToString());
context.Response.AddHeader("content-range", contentRange);
context.Response.AddHeader("last-modified", DateTime.Now.ToString("ddd, dd MMM yyyy hh:mm:ss") + " GMT");
byte[] bytes = Encoding.ASCII.GetBytes(string.Format("{0} : {1}", fi.Name, fi.LastAccessTimeUtc));
string eTag = Convert.ToBase64String(MD5CryptoServiceProvider.Create().ComputeHash(bytes));
context.Response.AddHeader("ETag", string.Format("\"{0}\"", eTag));
context.Response.StatusCode = ; //partial content context.Response.TransmitFile(file, long.Parse(parts[]), contentLength);
context.Response.Flush();
}
else //request is not for byte range
{
context.Response.AddHeader("content-length", fileLength.ToString()); if (chunkSize <= )
{
context.Response.TransmitFile(file);
context.Response.Flush();
}
else
{
long index = ;
while (true)
{
if (index >= fileLength)
break;
if (index < fileLength && index + chunkSize > fileLength)
{
context.Response.TransmitFile(file, index, fileLength - index);
context.Response.Flush();
break;
} context.Response.TransmitFile(file, index, chunkSize);
context.Response.Flush();
index += chunkSize;
}
}
}
}
catch (Exception ex)
{
context.Response.ClearHeaders();
context.Response.ClearContent();
context.Response.Clear(); context.Response.StatusCode = ;
context.Response.Write("Download Error: " + ex.GetBaseException().Message);
context.Response.Flush();
//throw ex;
}
finally
{
context.Response.End();
//context.ApplicationInstance.CompleteRequest();
}
} public bool IsReusable
{
get { return false; }
}
}

C# IOThread的更多相关文章

  1. mysql半同步(semi-sync)源码实现

    mysql复制简单介绍了mysql semi-sync的出现的原因,并说明了semi-sync如何保证不丢数据.这篇文章主要侧重于semi-sync的实现,结合源码将semi-sync的实现过程展现给 ...

  2. Java NIO浅析

    NIO(Non-blocking I/O,在Java领域,也称为New I/O),是一种同步非阻塞的I/O模型,也是I/O多路复用的基础,已经被越来越多地应用到大型应用服务器,成为解决高并发与大量连接 ...

  3. kafka源码分析之二客户端分析

    客户端由两种:生产者和消费者 1. 生产者 先看一下生产者的构造方法: private KafkaProducer(ProducerConfig config, Serializer<K> ...

  4. MySQL复制配置(多主一从)

    复制多主一从 replicaion 原理 复制有三个步骤:(分为三个线程 slave:io线程 sql线程 master:io线程) 1.master将改变记录到二进制日志(binary log)中( ...

  5. Unix调试工具dbx使用方法

     dbx 命令 用途 提供了一个调试和运行程序的环境. 语法 dbx [ -a ProcessID ] [ -c CommandFile ] [ -d NestingDepth ] [ -I Dire ...

  6. kafka producer源码

    producer接口: /** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor l ...

  7. iOS AudioQueue机制的延迟问题探究

    关键字:VOIP,AudioUnit,AudioQueue,RemoteIO问题描述VOIP通话,iOS底层音频方式采用AudioUnit机制,本来也挺好,但是会有遇到CS域来电时RemoteIO挂死 ...

  8. 从MySQL 5.5到5.7看复制的演进

    概要:MySQL 5.5 支持单线程模式复制,MySQL 5.6 支持库级别的并行复制,MySQL 5.7 支持事务级别并行复制.结合这个主线我们可以来分析一下MySQL以及社区发展的一个前因后果. ...

  9. 微博MySQL优化之路--dockone微信群分享

    微博MySQL优化之路 数据库是所有架构中不可缺少的一环,一旦数据库出现性能问题,那对整个系统都回来带灾难性的后果.并且数据库一旦出现问题,由于数据库天生有状态(分主从)带数据(一般还不小),所以出问 ...

随机推荐

  1. PHP算法排序之快速排序、冒泡排序、选择排序、插入排序性能对比

    <?php //冒泡排序 //原理:从倒数第一个数开始,相邻的两个数比较,后面比前面的小,则交换位置,一直到比较第一个数之后则最小的会排在第一位,以此类推 function bubble_sor ...

  2. AtCoder Grand Contest 017D (AGC017D) Game on Tree 博弈

    原文链接https://www.cnblogs.com/zhouzhendong/p/AGC017D.html 题目传送门 - AGC017D 题意 给定一棵 n 个节点的以节点 1 为根的树. 两个 ...

  3. 【转】科大校长给数学系学弟学妹的忠告&本科数学参考书

    1.老老实实把课本上的题目做完.其实说科大的课本难,我以为这话不完整.科大的教材,就数学系而言还是讲得挺清楚的,难的是后面的习题.事实上做1道难题的收获是做10道简单题所不能比的. 2.每门数学必修课 ...

  4. 给linux服务器添加一块新的磁盘

    http://www.linuxidc.com/Linux/2011-02/31868.htm 把硬盘装好后,我们用 fdisk -l 查看下: 图中可以看出 /dev/sdb 是500G,新加的硬盘 ...

  5. CentOS 7.2配置Apache服务httpd小伙伴们可以参考一下

    这篇文章主要为大家详细介绍了CentOS 7.2配置Apache服务 httpd上篇,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 一.Perl + mod_perl 安装mod_perl使Per ...

  6. P1025 数的划分 dfs dp

    题目描述 将整数nn分成kk份,且每份不能为空,任意两个方案不相同(不考虑顺序). 例如:n=7n=7,k=3k=3,下面三种分法被认为是相同的. 1,1,51,1,5;1,5,11,5,1;5,1, ...

  7. C++实现--最大公因数和最小公倍数

    一丶 最大公因数求法: 辗转相除法(也称欧几里得算法)原理:   二丶最小公倍数求法:两个整数的最小公倍数等于两整数之积除以最大公约数   C++ 代码实现 #include <iostream ...

  8. fastadmin系统配置

    常规管理--->系统配置--->字典配置-->配置分组-->追加--填上键值-->回车 然后在点上图的+添加自定义的配置项(如果需要删除配置项,需要删除数据库中fa_co ...

  9. POJ 1904 King's Quest (强连通分量+完美匹配)

    <题目链接> 题目大意: 有n个王子,每个王子都有k个喜欢的妹子,每个王子只能和喜欢的妹子结婚,大臣给出一个匹配表,每个王子都和一个妹子结婚,但是国王不满意,他要求大臣给他另一个表,每个王 ...

  10. componentWillReceiveProps详解(this.props)状态改变检测机制

    参考资料:http://blog.csdn.net/ElinaVampire/article/details/51813677 大家先看一张关于组件挂载的经典的图片: 下面一一说一下这几个生命周期的意 ...