如果设计一个服务器程序,每当处理用户请求时,都开始一个线程,将会在一定程序上消耗服务器的资源。为此,一个最好的解决方法就是在服务器启动之前,事先创建一些线程对象,然后,当处理客户端请求时,就从这些建好的线程中获得线程对象,并处理请求。保存这些线程对象的结构就叫做线程池。

在C#中可以通过System.Threading.ThreadPool类来实现,在默认情况下,ThreadPool最大可建立500个工作线程和1000个I/O线程(根据机器CPU个数和.net framework版本的不同,这些数据可能会有变化)。下面是一个用C#从线程池获得线程的例子:
private static void execute(object state)
{
    Console.WriteLine(state);      
}
static void Main(string[] args)
{
  
    int workerThreads;
    int completionPortThreads;
         
    ThreadPool.GetMaxThreads(out workerThreads, out completionPortThreads);
    Console.WriteLine(workerThreads);
    Console.WriteLine(completionPortThreads);    
    ThreadPool.QueueUserWorkItem(execute,"线程1");   // 从线程池中得到一个线程,并运行execute
    ThreadPool.QueueUserWorkItem(execute, "线程2");
    ThreadPool.QueueUserWorkItem(execute, "线程3");
    Console.ReadLine();
}
下图为上面代码的运行结果。
 
要注意的是,使用ThreadPool获得的线程都是后台线程。
下面的程序是我设计的一个下载文件服务器的例子。这个例子从ThreadPool获得线程,并处理相应的客户端请求。

 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.IO;

namespace MyThread
{
    class FileServer
    {
        private String root;
        private Thread listenerThread;

private void worker(object state)
        {
             TcpClient client = state as TcpClient;
             try
             {

client.ReceiveTimeout = 2000;
                 Stream stream = client.GetStream();
                 System.IO.StreamReader sr = new StreamReader(stream);
                 String line = sr.ReadLine();
                 String[] array = line.Split(' ');
                 String path = array[1].Replace('/', '\\');
                 String filename = root + path;
                 if (File.Exists(filename))  // 如果下载文件存在,开始下载这个文件
                 {
                     FileStream fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read, 
                                                           FileShare.Read);
                     byte[] buffer = new byte[8192]; // 每次下载8K
                     int count = 0;
                     String responseHeader = "HTTP/1.1 200 OK\r\n" +
                                             "Content-Type:application/octet-stream\r\n" +
                                             "Content-Disposition:attachment;filename=" +
                                                   filename.Substring(filename.LastIndexOf("\\") + 1) + "\r\n\r\n";
                     byte[] header = ASCIIEncoding.ASCII.GetBytes(responseHeader);
                     stream.Write(header, 0, header.Length);
                     while ((count = fileStream.Read(buffer, 0, buffer.Count())) > 0)
                     {
                         stream.Write(buffer, 0, count);
                     }
                     Console.WriteLine(filename + "下载完成");
                 }
                 else  // 文件不存在,输出提示信息
                 {
                     String response = "HTTP/1.1 200 OK\r\nContent-Type:text/plain;charset=utf-8\r\n\r\n文件不存在";
                     byte[] buffer = ASCIIEncoding.UTF8.GetBytes(response);
                     stream.Write(buffer, 0, buffer.Length);
                 }

}
             catch (Exception e)
             {
                 Console.WriteLine(e.Message);
             }
             finally
             {
                 if (client != null)
                 {
                     client.Close();
                 }
             }
        }

private void listener()
        {
            TcpListener listener = new TcpListener(1234);
            listener.Start();  // 开始监听客户端请求
            TcpClient client = null;

while (true)
            {
                client = listener.AcceptTcpClient();
                client.ReceiveTimeout =2000;
                ThreadPool.QueueUserWorkItem(worker, client);  // 从线程池中获得一个线程来处理客户端请求
            }
        }
        public FileServer(String root)
        {
            this.root= root;         
        }
        public void start()
        {
            listenerThread = new Thread(listener);
            listenerThread.Start();  // 开始运行监听线程
        }
    }
}

FileServer类的使用方法:

FileServer fs = new FileServer(“d:\\download”);

fs.start(); // 端口为1234
如果d:"download目录中有一个叫aa.exe的文件,在浏览器中输入如下的地址可下载:
    http://localhost:1234/aa.exe
下图为下载对话框:
要注意的是,本程序并没有处理含有中文和其他特殊字符(如空格)的url,因为,文件名要为英文名(不能有空格等特殊字符)。
本文转自 androidguy 51CTO博客,原文链接:http://blog.51cto.com/androidguy/216664,如需转载请自行联系原作者

C#线程系列讲座(3):线程池和文件下载服务器相关推荐

  1. C#线程系列讲座(4):同步与死锁

    本文为原创,如需转载,请注明作者和出处,谢谢! 上一篇:C#线程系列讲座(3):线程池和文件下载服务器   虽然线程可以在一定程度上提高程序运行的效率,但也会产生一些副作用.让我们先看看如下的代码: ...

  2. C#线程系列讲座(1):BeginInvoke和EndInvoke方法

    本文是转载,原文地址:http://www.itpub.net/thread-1021075-1-1.html 几个关键词:IAsyncResult,isComplete,asyncResult.As ...

  3. C#线程系列(3):线程池和文件下载服务器

        如果设计一个服务器程序,每当处理用户请求时,都开始一个线程,将会在一定程序上消耗服务器的资源.为此,一个最好的解决方法就是在服务器启动之前,事先创建一些线程对象,然后,当处理客户端请求时,就从 ...

  4. 【java线程系列】java线程系列之java线程池详解

    一线程池的概念及为何需要线程池: 我们知道当我们自己创建一个线程时如果该线程执行完任务后就进入死亡状态,这样如果我们需要在次使用一个线程时得重新创建一个线程,但是线程的创建是要付出一定的代价的,如果在 ...

  5. java线程协作_java线程系列之三(线程协作)

    上一篇讲述了线程的互斥(同步),但是在很多情况下,仅仅同步是不够的,还需要线程与线程协作(通信),生产者/消费者问题是一个经典的线程同步以及通信的案例.该问题描述了两个共享固定大小缓冲区的线程,即所谓 ...

  6. C#线程系列讲座(5):同步技术之Monitor

    在上一讲介绍了使用lock来实现线程之间的同步.实际上,这个lock是C#的一个障眼法,在C#编译器编译lock语句时,将其编译成了调用Monitor类.先看看下面的C#源代码: public sta ...

  7. java 手编线程池_死磕 java线程系列之自己动手写一个线程池

    欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. (手机横屏看源码更方便) 问题 (1)自己动手写一个线程池需要考虑哪些因素? (2)自己动手写 ...

  8. Java多线程系列(五):线程池的实现原理、优点与风险、以及四种线程池实现

    为什么需要线程池 我们有两种常见的创建线程的方法,一种是继承Thread类,一种是实现Runnable的接口,Thread类其实也是实现了Runnable接口.但是我们创建这两种线程在运行结束后都会被 ...

  9. winform判断线程有没有完成_并发编程系列1:线程池的架构实现、大小配置、及四种线程池使用...

    △ 公众号回复关键词"架构" 即可领取<1500+BAT架构及面试专题合集> 本篇为线程池系列文章之一,不经常使用线程池的童鞋,还有对几种线程的使用不甚了解的童鞋,可以 ...

最新文章

  1. flink集成springboot案例_Flink从流处理到流批一体的19个最佳实践
  2. redis搭建集群和主从
  3. 向下钻取按钮位置设置
  4. spark 简单实战_大数据入门与实战-Spark上手
  5. Javaweb编程中的乱码问题
  6. Kafka2.0生产者客户端使用
  7. 门限回归模型的思想_Stata+R:门槛回归教程
  8. js获取对象数组中指定属性值对象_3分钟学会操作JavaScript内置对象,快来试试吧...
  9. mysql索引条件下推_MySQL 索引条件下推优化
  10. 让你此生难成大器的七宗罪
  11. 在VFP里玩SQL查询
  12. ARM指令集之跳转指令
  13. 剑指Offer——完美+今日头条笔试题+知识点总结
  14. Maya粒子消散特效制作(二)
  15. linux安装git lfs
  16. visio修改默认字体
  17. 和大学说再见,却不跟青春道别
  18. 巨蟹座---永远的伤
  19. Phoenix官方教程 (九) Channel
  20. 2014暑假ACM13级一批集训内容

热门文章

  1. H3CNE考试讨论群
  2. 八种反应表示员工认可你
  3. 创建用于 ASP.NET 的分页程序控件
  4. C语言入门经典——基础知识(指针 数组 多维数组)
  5. python进阶:JSON与枚举
  6. 剑指offer 算法 (代码的鲁棒性)
  7. 51nod 1402最大值
  8. pl/sql developer执行光标所在行
  9. MySQL-查询结果缓存
  10. emca 更改监听端口