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

简介:
如果设计一个服务器程序,每当处理用户请求时,都开始一个线程,将会在一定程序上消耗服务器的资源。为此,一个最好的解决方法就是在服务器启动之前,事先创建一些线程对象,然后,当处理客户端请求时,就从这些建好的线程中获得线程对象,并处理请求。保存这些线程对象的结构就叫做线程池。
     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,如需转载请自行联系原作者

相关文章
|
15天前
|
Java 数据库 Android开发
【专栏】Kotlin在Android开发中的多线程优化,包括线程池、协程的使用,任务分解、避免阻塞操作以及资源管理
【4月更文挑战第27天】本文探讨了Kotlin在Android开发中的多线程优化,包括线程池、协程的使用,任务分解、避免阻塞操作以及资源管理。通过案例分析展示了网络请求、图像处理和数据库操作的优化实践。同时,文章指出并发编程的挑战,如性能评估、调试及兼容性问题,并强调了多线程优化对提升应用性能的重要性。开发者应持续学习和探索新的优化策略,以适应移动应用市场的竞争需求。
|
3天前
|
设计模式 监控 Java
Java多线程基础-11:工厂模式及代码案例之线程池(一)
本文介绍了Java并发框架中的线程池工具,特别是`java.util.concurrent`包中的`Executors`和`ThreadPoolExecutor`类。线程池通过预先创建并管理一组线程,可以提高多线程任务的效率和响应速度,减少线程创建和销毁的开销。
22 2
|
15天前
|
Java 数据库
【Java多线程】对线程池的理解并模拟实现线程池
【Java多线程】对线程池的理解并模拟实现线程池
22 1
|
15天前
|
Java 程序员 数据库
Java线程池让使用线程变得更加高效
使用一个线程需要经过创建、运行、销毁三大步骤,如果业务系统每个线程都要经历这个过程,那会带来过多不必要的资源消耗。线程池就是为了解决这个问题而生,需要时就从池中拿取,使用完毕就放回去,池化思想通过复用对象大大提高了系统的性能。线程池、数据库连接池、对象池等都采用了池化技术,下面我们就来学习下线程池的核心知识、面试重点~
63 5
Java线程池让使用线程变得更加高效
|
2天前
|
设计模式 安全 Java
Java 多线程系列Ⅳ(单例模式+阻塞式队列+定时器+线程池)
Java 多线程系列Ⅳ(单例模式+阻塞式队列+定时器+线程池)
|
3天前
|
存储 Java 调度
Java多线程基础-11:工厂模式及代码案例之线程池(二)
这篇内容介绍了Java多线程基础,特别是线程池中的定时器和拒绝策略。
17 0
|
6天前
|
安全 Java 容器
线程安全问题、同步代码块、同步方法、线程池详解
线程安全问题、同步代码块、同步方法、线程池详解
17 0
|
8天前
|
设计模式 安全 Java
【Linux 系统】多线程(生产者消费者模型、线程池、STL+智能指针与线程安全、读者写者问题)-- 详解
【Linux 系统】多线程(生产者消费者模型、线程池、STL+智能指针与线程安全、读者写者问题)-- 详解
|
15天前
|
消息中间件 监控 前端开发
面试官:核心线程数为0时,线程池如何执行?
线程池是 Java 中用于提升程序执行效率的主要手段,也是并发编程中的核心实现技术,并且它也被广泛的应用在日常项目的开发之中。那问题来了,如果把线程池中的核心线程数设置为 0 时,线程池是如何执行的? 要回答这个问题,我们首先要了解在正常情况下,线程池的执行流程,也就是说当有一个任务来了之后,线程池是如何运行的? ## 1.线程池的执行流程 正常情况下(核心线程数不为 0 的情况下)线程池的执行流程如下: 1. **判断核心线程数**:先判断当前工作线程数是否大于核心线程数,如果结果为 false,则新建线程并执行任务。 2. **判断任务队列**:如果大于核心线程数,则判断任务队列是否
33 1
面试官:核心线程数为0时,线程池如何执行?
|
15天前
|
开发框架 前端开发 JavaScript
C# 结合JavaScript实现手写板签名并上传到服务器
C# 结合JavaScript实现手写板签名并上传到服务器