多线程

前言

多线程简单来说就是让计算机进行“一心多用”,不过此处一心多用并不是真正的一心多用,只是在不同的时间片段(极短的时间片段内)分别执行不同的操作,但在单位时间片段内饰一心一用。简单举个例子,周伯通让小龙女学习左右互搏,让她一手画方一手画圆,做到一心两用。那我们来简单分析一下,如果不是同时结束呢,我们先这样来,左手先画四分之一圆,完成后右手再画正方形的一条边,然后依次类推,知道方圆均画完。这样属于两个手分别操作,那么我们进一步细分,左后先画八分之一圆,右手再画八分之一正方形,当然这样还是两个手分别操作,那么我们整个圆和方形分成一百步画完呢,假如分成一千步呢?分成一千步即使每一步都是单独的在画圆或者或方,但是总体上方圆基本同时在进行。这就是多线程所展现给我们的,但是多线程的意义就是当某一线程阻塞了,不会导致整个程序等待。加入我们只会用一只手画方圆,先画圆后画方,那么在画圆的过程中加入手抽筋了,那我们都要在等待手恢复,如果一手画方一手画圆,那么左手即使抽筋了,只是影响画圆而不影响右手画方。这就是多线程的意义。下面讲解三种最基本的开启线程方法,讲解过程中为了分别讲解,只引用了部分代码,最后会给出完整代码,建议可以边运行边看分步讲解。

1.基本方法

引入命名空间System.Threading,最基本在简洁的方法ThreadStart/ParameterizedThreadStart,前者不带参数,后者可以给方法传递一个参数(带参数的方法入口参数必须为object类型),但是最基本的方法,开启线程比较随意,容易混乱而且开启过多线程也会影响性能。开启线程不过是在主程序运行或者其他方法运行时,开启其他方法,既然开启其他就必然用得到委托,本质上还是向thread类中传递要开启的方法,具体开启线程有thread类完成。

1.0 子线程运行的方法

static void AsyncDemo()//无参数的方法
        {
            for(int i=0;i<=10;i++)
            {
                Thread.Sleep(100);
                Console.WriteLine("Async thread : " + i);
            }
        }
        static void AsyncDemo(object obj)//有参数的方法
        {
            string _obj = (string)obj;
            Console.WriteLine(_obj);
            for (int i = 0; i <= 10; i++)
            {
                Thread.Sleep(100);
                Console.WriteLine("Async thread : " + i);
            }
        }

1.1 开启线程

            //Thread thread = new Thread(new ThreadStart(AsyncDemo));
            //thread.Start();
            string para = "Hello World!";
            Thread thread = new Thread(new ParameterizedThreadStart(AsyncDemo));
            thread.Start(para);

1.2 主程序运行内容

           //主程序
            for (int i=0;i<=10;i++)
            {
                Thread.Sleep(100);
                Console.WriteLine("Main thread : " + i);
            }

本例子通过两个for语句来分别假定主程序方法和线程方法,如果不用线程,会一个循环执行完在执行另一个循环,如果没有Thread.Sleep(100)则体现不出同时运行的效果,因为就是在极短的时间片段内,每一个循环都可以执行完,所以为了达到效果,让每一个都暂停0.1s。

2.线程池

在程序中随意开启线程会影响程序的质量,所以C#提供了线程池来管理线程,即通过QueueUserWorkItem来开启线程,他有两个重载,可以传递参数给开启线程的方法。开启线程的方法还是采用1、中方法,线程开启如下:

            string str = "Hello World!";
            ThreadPool.SetMaxThreads(1000, 1000);//定义处于活动状态的线程池的请求数目
            ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncDemo),str);
           // ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncDemo));//无参数的方法

3.带返回值方法

以上两种方法都没有返回值,若一个方法有返回值,则需要其他方法来实现。开启线程本质上还是通过委托传递一个方法(函数),而委托也为我们提供了相应开启线程的方法。当我们定义好委托以后,在码代码的时候平台会自动给我们提示它可能存在的方法,其中BeginInvoke(name, null, null)方法就是来开启线程,第一个参数为方法所需要的入口参数,第二个为回调函数,其参数必须为一个IAsyncResult 接口,第三个为 为回调函数传入的参数。后两个参数可以为空,它返回一个IAsyncResult类型的接口,通过此接口传入EndInvoke(result)方法即可获取返回值。下面为具体步骤:

3.0 建立委托

public delegate string Mydeletegate(string str);

3.1 定义相关方法

static string AsyncDemo(string obj)
        {
            string _obj = (string)obj;
            Console.WriteLine(_obj);
            for (int i = 0; i <= 10; i++)
            {
                Thread.Sleep(100);
                Console.WriteLine("Async thread : " + i);
            }
            return "Hello " + obj;
        }

3.2 委托指向方法并开启线程

IAsyncResult result = de.BeginInvoke(name, null, null);
 for (int i = 0; i <= 10; i++)
            {
                Thread.Sleep(100);
                Console.WriteLine("Main thread : " + i);
            }

            string redata = de.EndInvoke(result);
Console.WriteLine(redata);

但是此方法会存在一个问题,即如果把上述代码中的redata那一语句放在for循环之前,则会先执行型委托指向的方法的循环,后执行主程序(可以自行测试),因为主程序在等待执行完redata=。。。。后再执行for语句,所以可以充分利用接口IAsyncResult提供的方法或者字段。也可以采用BeginInvoke方法的第二个参数,回调函数,即把EndInvoke方法放入回调函数中,本例的回调函数为Completed。回调函数如下

        static void Completed(IAsyncResult res)
        {
            string str = (string)res.AsyncState;
            AsyncResult _res = (AsyncResult)res;
            Mydeletegate de1 = (Mydeletegate)_res.AsyncDelegate;
            string data = de1.EndInvoke(res);
            Console.WriteLine(data);
            Console.WriteLine(str);
        }

对相关参数进行一下解释:通过接口中的AsyncState可以获取异步操作传入的信息,即BeginInvoke的第三个参数,为object类型的,要转化成所用的类型。我们要在此函数中完成EndInvoke就需要获取BeginInvoke的委托,查看元数据可以知道AsyncResult 继承了IAsyncResult 接口,AsyncResult中的AsyncDelegate属性可以获取相关委托,所以先把传入的IAsyncResult 转化为AsyncResult 类型,在通过AsyncDelegate获取到委托(其为object类型,需要转化为委托类型),然后调用EndInvoke方法,获取返回值, 主程序如下:

de.BeginInvoke(name, new AsyncCallback(Completed), index);

            for (int i = 0; i <= 10; i++)
            {
                Thread.Sleep(100);
                Console.WriteLine("Main thread : " + i);
            }

在最后附上三种方法的完整程序,这三种方法为最基本的方法,在以后不管是网络请求还是文件I/O的异步编程都与委托类方法一样。

4.完整代码

4.1 基本方法代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace MyThread
{
    class Program
    {
        static void AsyncDemo()//无参数的方法
        {
            for(int i=0;i<=10;i++)
            {
                Thread.Sleep(100);
                Console.WriteLine("Async thread : " + i);
            }
        }
        static void AsyncDemo(object obj)//有参数的方法
        {
            string _obj = (string)obj;
            Console.WriteLine(_obj);
            for (int i = 0; i <= 10; i++)
            {
                Thread.Sleep(100);
                Console.WriteLine("Async thread : " + i);
            }
        }
        static void Main(string[] args)
        {
            //无参数方法
            //Thread thread = new Thread(new ThreadStart(AsyncDemo));
            //thread.Start();
            string para = "Hello World!";
            Thread thread = new Thread(new ParameterizedThreadStart(AsyncDemo));
            thread.Start(para);

            //主程序
            for (int i=0;i<=10;i++)
            {
                Thread.Sleep(100);
                Console.WriteLine("Main thread : " + i);
            }
            Console.ReadKey();
        }
    }
}

4.2 线程池代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace ThreadPoolTest
{
    class Program
    {
        static void AsyncDemo()
        {
            for (int i = 0; i <= 10; i++)
            {
                Thread.Sleep(100);
                Console.WriteLine("Async thread : " + i);
            }
        }
        static void AsyncDemo(object obj)
        {
            string _obj = (string)obj;
            Console.WriteLine(_obj);
            for (int i = 0; i <= 10; i++)
            {
                Thread.Sleep(100);
                Console.WriteLine("Async thread : " + i);
            }
        }
        static void Main(string[] args)
        {
            string str = "Hello World!";
            ThreadPool.SetMaxThreads(1000, 1000);//定义处于活动状态的线程池的请求数目
            ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncDemo),str);
           // ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncDemo));//无参数的方法


            for (int i = 0; i <= 10; i++)
            {
                Thread.Sleep(100);
                Console.WriteLine("Main thread : " + i);
            }
            Console.ReadKey();
        }
    }
}

4.3 返回值线程

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Runtime.Remoting.Messaging;

namespace Deletegatetest
{
    public delegate string Mydeletegate(string str);

    class Program
    {

        static string AsyncDemo(string obj)
        {
            string _obj = (string)obj;
            Console.WriteLine(_obj);
            for (int i = 0; i <= 10; i++)
            {
                Thread.Sleep(100);
                Console.WriteLine("Async thread : " + i);
            }
            return "Hello " + obj;
        }
        //回调函数
        static void Completed(IAsyncResult res)
        {
            string str = (string)res.AsyncState;
            AsyncResult _res = (AsyncResult)res;
            Mydeletegate de1 = (Mydeletegate)_res.AsyncDelegate;
            string data = de1.EndInvoke(res);
            Console.WriteLine(data);
            Console.WriteLine(str);
        }

        static void Main(string[] args)
        {
            string name = "Andy";
            string index = "Index!";
            Mydeletegate de = new Mydeletegate(AsyncDemo);
            
            //IAsyncResult result = de.BeginInvoke(name, null, null);
            de.BeginInvoke(name, new AsyncCallback(Completed), index);

            for (int i = 0; i <= 10; i++)
            {
                Thread.Sleep(100);
                Console.WriteLine("Main thread : " + i);
            }

            //string redata = de.EndInvoke(result);
            //Console.WriteLine(redata);

            //for (int i = 0; i <= 10; i++)
            //{
            //    Thread.Sleep(100);
            //    Console.WriteLine("Main thread : " + i);
            //}
            Console.ReadKey();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值