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

在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,因为,文件名要为英文名(不能有空格等特殊字符)。

C#线程系列讲座(3):线程池和文件下载服务器的更多相关文章

  1. 线程系列08,实现线程锁的各种方式,使用lock,Montor,Mutex,Semaphore以及线程死锁

    当涉及到多线程共享数据,需要数据同步的时候,就可以考虑使用线程锁了.本篇体验线程锁的各种用法以及线程死锁.主要包括: ※ 使用lock处理数据同步※ 使用Monitor.Enter和Monitor.E ...

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

    转载自:http://www.cnblogs.com/levin9/articles/2319248.html 开发语言:C#3.0IDE:Visual Studio 2008本系列教程主要包括如下内 ...

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

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

  4. C#线程系列讲座(2):Thread类的应用

    一.Thread类的基本用法 通过System.Threading.Thread类可以开始新的线程,并在线程堆栈中运行静态或实例方法.可以通过Thread类的的构造方法传递一个无参数,并且不返回值(返 ...

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

    一.C#线程概述 在操作系统中一个进程至少要包含一个线程,然后,在某些时候需要在同一个进程中同时执行多项任务,或是为了提供程序的性能,将要执行的任务分解成多个子任务执行.这就需要在同一个进程中开启多个 ...

  6. 【java线程系列】java线程系列之线程间的交互wait()/notify()/notifyAll()及生产者与消费者模型

    关于线程,博主写过java线程详解基本上把java线程的基础知识都讲解到位了,但是那还远远不够,多线程的存在就是为了让多个线程去协作来完成某一具体任务,比如生产者与消费者模型,因此了解线程间的协作是非 ...

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

    本文来自:高爽|Coder,原文地址:http://blog.csdn.net/ghsau/article/details/7433673,转载请注明. 上一篇讲述了线程的互斥(同步),但是在很多情况 ...

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

    虽然线程可以在一定程度上提高程序运行的效率,但也会产生一些副作用.让我们先看看如下的代码:     class Increment     {         private int n = 0;   ...

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

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

随机推荐

  1. ORA-12518: TNS: 监听程序无法分发客户机连接

    在团队成员增多时,经常出现“无法分发客户端连接”等问题.在网上搜索一番后,最终解决了该问题,现将解决方案总结如下,以供参考和以后备用. 原因:团队成员增多,原有数据库设置不够用,导致连接plsql和启 ...

  2. Spring Project Annotations

       Project  Annotation  Discovered By  Package     Target(s)  Parameters  Notes . AspectJ @EnableSpr ...

  3. [转]理解OAuth 2.0

    作者: 阮一峰 OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版. 本文对OAuth 2.0的设计思路和运行流程,做一个简明通俗的解释, ...

  4. TCP协议中的三次握手和四次挥手(图解)(转载http://blog.csdn.net/whuslei/article/details/6667471)

    建立TCP需要三次握手才能建立,而断开连接则需要四次握手.整个过程如下图所示: 先来看看如何建立连接的 首先Client端发送连接请求报文,Server段接受连接后回复ACK报文,并为这次连接分配资源 ...

  5. selenium测试套件

    1.测试套件测试套件,简单理解就是讲多个用例,装在一个容器里来同时执行完成. 2.测试套件分析 #coding=utf-8 import unittestimport BaiDuSearch,BaiD ...

  6. Oracle数值处理函数 (绝对值、取整...)

    1.绝对值:abs()    select abs(-2) value from dual; 2.取整函数(大):ceil()    select ceil(-2.001) value from du ...

  7. 高德地图API应用

    高德地图官网:http://api.amap.com/javascript/ 输入关键字,搜索地址功能的网页: 1.引用远程Map Api(js)网址形式(注册后获取) 2.定义个<div> ...

  8. 蓝牙的AVDTP协议笔记

    1.概述    AVDTP(AUDIO/VIDEO DISTRIBUTION TRANSPORT PROTOCOL)是用来描述音频/视频在蓝牙设备间的传输的协议,是A2DP协议的基础协议,其在协议栈中 ...

  9. 回退(pop&present)到根页面(根控制器)的方法,很不错~

    http://blog.csdn.net/assholeu/article/details/45897035

  10. 设计模式:状态模式(State)

    定  义:当一个对象内在状态改变时允许改变其行为,这个对象看起来像是改变了其类. 状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况, 把状态的判断逻辑转移到表示不同状态的一系列子 ...