Chinaunix首页 | 论坛 | 博客
  • 博客访问: 274213
  • 博文数量: 81
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 878
  • 用 户 组: 普通用户
  • 注册时间: 2014-07-25 23:20
文章分类

全部博文(81)

文章存档

2017年(45)

2016年(20)

2015年(2)

2014年(14)

我的朋友

分类: C#/.net

2016-12-23 16:58:13

一、多线程和异步的区别

多线程和异步操作两者都可以达到避免调用线程阻塞的目的。但是,多线程和异步操作还是有一些区别的。而这些区别造成了使用多线程和异步操作的时机的区别。

  • 线程本质上是进程中一段并发运行的代码,所以线程需要操作系统投入CPU资源来运行和调度;

  • 异步本质上是计算机硬件功能,其操作无须消耗CPU时间的I/O操作;

  • 编写异步操作的复杂程度较高,程序主要使用回调方式进行处理,处理函数可以不必使用共享变量;

  • 多线程是顺序执行,编程简单。但是线程的使用(滥用)会给系统带来上下文切换的额外负担。并且线程间的共享变量可能造成死锁的出现。

当需要执行I/O操作时,使用异步操作比使用线程+同步 I/O操作更合适。I/O操作不仅包括了直接的文件、网络的读写,还包括数据库操作、Web Service、HttpRequest以及.net Remoting 等跨进程的调用。

线程适用那种需要长时间 CPU 运算的场合,例如耗时较长的图形处理和算法执行。

更多关于多线程和异步的异同可以参考 。


二、多线程编程

刚好这段时间在看网络编程,在这里就结合多线程和网络编程,实现能够应对多客户端请求的服务端。Socket 类的 Accept() 方法一直等待,直到有客户端连接请求。Socket 网络编程可以参考

static Socket client;
static void Main(string[] args)
{
    IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9060);    //创建 IPEndPoint 对象,表示接受任何端口 9050 的客户机地址
    Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);    // TCP 
    server.Bind(ipep);    //绑定
    server.Listen(20);    //监听
    Console.WriteLine("正在监听...");

    //下面这段代码阻塞,可以用新线程执行,但可能会出问题
    while (true)
    {
        client = server.Accept();    //收到客户端请求

        //开新线程发送数据
        Thread recvthread = new Thread(sendData);
        recvthread.IsBackground = true;    //后台线程
        // 启动消息服务线程
        recvthread.Start();
        ///也可以开其他线程,如接收数据线程
   }
}

static private void sendData()
{
    if (client != null)
    {
        Console.WriteLine("客户机" + client.RemoteEndPoint + "连接到服务器");
        string data = "http://blog.csdn.net/xgf415/article/details/hello client";
        byte[] msg = System.Text.Encoding.ASCII.GetBytes(data);    //将 string 转化为 byte 数组
        client.Send(msg);    //向客户端发生数据
        Console.WriteLine("发生数据:" + data);
        client.Close();
    }   
}

三、异步编程

基于异步的编程方法有三种:

  • 异步编程模式(APM),其中异步操作要求 Begin 和 End 方法(例如,异步写操作的 BeginWrite 和 EndWrite)。对于新的开发工作不再建议采用此模式。
  • 基于事件的异步模式 (EAP) 需要一个具有 Async 后缀的方法,还需要一个或多个事件、事件处理程序、委托类型和 EventArg 派生的类型,在 .NET Framework 2.0 版中引入的。对于新的开发工作不再建议采用此模式。
  • 基于任务的异步模式 (TAP),该模式使用一个方法表示异步操作的启动和完成。.NET Framework 4 中引入了 TAP,并且是 .NET Framework 中异步编程的建议方法。

Task 异步,有以下三种方法创建 Task:

  • Task.Factory.StartNew,比较常用。

  • Task.Run,是.net 4.5中增加的。

  • Task.FromResult,如果结果是已计算,就可以使用这种方法来创建任务。

下面就以 Task.Factory.StartNew 进行异步编程

static Socket client;
static void Main(string[] args)
{
    IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9060);    //创建 IPEndPoint 对象,表示接受任何端口 9050 的客户机地址
    Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);    // TCP 
    server.Bind(ipep);    //绑定
    server.Listen(20);    //监听
    Console.WriteLine("正在监听...");

    //下面这段代码阻塞,可以放到子线程执行,但是可能会出现问题,可以看最后面分析
    while (true)
    {
        client = server.Accept();
        //创建并启动 task
        Task.Factory.StartNew(() =>
        {
            sendData();    //一个没有返回值的方法
        });
    }
}

static private void sendData()
{
    if (client != null)
    {
        Console.WriteLine("客户机" + client.RemoteEndPoint + "连接到服务器");
        string data = "http://blog.csdn.net/xgf415/article/details/hello client";
        byte[] msg = System.Text.Encoding.ASCII.GetBytes(data);    //将 string 转化为 byte 数组
        client.Send(msg);    //向客户端发生数据
        Console.WriteLine("发生数据:" + data);
        client.Close(); }   
}

使用 Async 与 Await 进行异步编程

static Socket client;
static Socket server;
static void Main(string[] args)
{
    IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9060);    //创建 IPEndPoint 对象,表示接受任何端口 9050 的客户机地址
    server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);    // TCP 
    server.Bind(ipep);    //绑定
    server.Listen(20);    //监听
    Console.WriteLine("正在监听...");

    accept();
}
static async void accept() 
{
    await acceptAsync();
}
static async Task acceptAsync()    //异步接受请求
{
    while (true)
    {
        client = server.Accept();    //收到客户端请求
        await sendData();
    }
}
static async Task sendData()    //异步发生数据
{
    if (client != null)
    {
        Console.WriteLine("客户机" + client.RemoteEndPoint + "连接到服务器");
        string data = "http://blog.csdn.net/xgf415/article/details/hello client";
        byte[] msg = System.Text.Encoding.ASCII.GetBytes(data);    //将 string 转化为 byte 数组
        client.Send(msg);    //向客户端发生数据
        //添加一个异步方法
        await Task.Delay(1000);  
        Console.WriteLine("发生数据:" + data);
        client.Close();
    }
}

由于 Main() 函数不能设置为 async 模式,所以增加了一个accept 函数,使用 await 来执行异步操作 acceptAsync() ,等待接受客户端的请求。同时在异步操作 acceptAsync() 中执行异步 sendData() ,异步发送数据。

一个问题:在上面的三个程序中,采用多线程和Task.Factory.StartNew 实现服务端的两个程序,如果把下面两段代码作为子线程或者异步函数执行,本来是阻塞的函数 client = server.Accept(),却没有等待客户端连接,直接执行过去了??是因为 accept()在同时在静态函数和多线程中的关系????(因为在非静态函数中这样并没有问题),但是使用Async 与 Await 执行的异步函数却能正常执行。

 while (true)
    {
        client = server.Accept();    //收到客户端请求

        //开新线程发送数据
        Thread recvthread = new Thread(sendData);
        recvthread.IsBackground = true;    //后台线程
        // 启动消息服务线程
        recvthread.Start();
        ///也可以开其他线程,如接收数据线程
   }
while (true)
{
    client = server.Accept();
    //创建并启动 task
    Task.Factory.StartNew(() =>
    {
        sendData();    //一个没有返回值的方法
    });
}
阅读(1666) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~