多线程如何排队执行

场景

有一个这样场景,程序会有一个非常耗时的操作,但要求耗时的操作完成后,再顺序的执行一个不耗时的操作,而且这个程序的调用,可能存在同时调用的情况。

具体的模型如下:

从Start开始触发了5个线程,经过一个longTimeJob同时执行,我们不关心longJob的执行时间和先后顺序,根据Start的先后顺序来执行一个ShortJob。下面我们用代码来模拟上面的过程。

举例说明:有ABCD 4个线程,进入的顺序也是ABCD,A耗时3s,B耗时7s,C耗时1s,D耗时3s. 所以如果当4个线程都同时开始执行时,完成的先后顺序为 CADB,但我们要求的顺序是ABCD,也就是说C要等待AB执行完后,才能继续后续的工作。

我们可以用请求bing搜索来模拟longTimeJob,根据传入的序列来决定请求多少次,主要模拟方法如下:

    private static async Task Test()
    {
        var arry = new[]
        {
            10, 9, 8, 7, 6, 5, 4, 3, 2, 1
        };
        var listTask = new List<Task<int>>();
        foreach (var i in arry)
        {
            var i1 = i;
            var task = Task.Run(() => DoJob(i1));

            listTask.Add(task);
        }

        foreach (var task in listTask)
        {
            Console.WriteLine("输出-->:" + await task);//
        }
    }     

    public static Task<int> DoJob(int o)
    {
        return Task.Run(() =>
        {
            DoLongTimeThing(o);
            return o;
        });
    }      

    public static void DoLongTimeThing(int i)
    {
        Console.WriteLine("执行-->:" + i);
        for (int j = 0; j < i; j++)
        {
            HttpGet("http://cn.bing.com/");
        }

        Console.WriteLine("执行完毕-->:" + i);
    }

    public static string HttpGet(string url)
    {
        try
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.Method = "GET";

            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
            string content = reader.ReadToEnd();
            return content;
        }
        catch (Exception e)
        {
            return e.Message;
        }

    }

执行结果:

上面的代码大概能解决我们的问题,有一个问题,对于客户的调用我们无法形成一个List,而且list是线程安全的,所以针对上述的方法在实际的业务场景中无法使用。

新思路

我们无法实现一个有序的Task列表,如果换一个角度考虑,当一个任务形成的时间,同时生成一个对应的HashCode,对HashCode进行一个队列的入队操作,当执行完成longTimeJob后,判断是不是队列的第一个Task的HashCode,如果是则执行,如果不是则继续等待,切换线程。 具体如下思路如下图:

  public static Queue<string> Queue = new Queue<string>();
	static void Main(string[] args)
	{
	    var arry = new[]
	    {
	        10, 9, 8, 7, 6, 5, 4, 3, 2, 1
	    };

	    foreach (var i in arry)
	    {
	        Console.WriteLine("进入Job顺序-->:" + i);
	        Test(i);
	    }
	    Console.ReadKey();
	}

	public static void Test(int i)
	{
	    var taskId = Guid.NewGuid().ToString();
	    Queue.Enqueue(taskId);
	    Task.Factory.StartNew(DoJob, new object[] { i, taskId });
	}
	public static void DoJob(object o)
	{
	    var oArry = (object[])o;
	    var n = (int)oArry[0];
	    var currId = oArry[1].ToString();

	    DoLongTimeThing(n);//

	    while (currId != Queue.Peek())
	    {
	        Thread.Sleep(1);//等线程切换
	    }

	    Console.WriteLine("DoShortJob输出-->:" + n);//
	    //请求数据库 
	    Queue.Dequeue();
	}
	public static void DoLongTimeThing(int i)
	{
	    Console.WriteLine("LongTimeJob执行-->:" + i);
	    for (int j = 0; j < i; j++)
	    {
	        HttpGet("http://cn.bing.com/");
	    }
	    Console.WriteLine("LongTimeJob执行完毕-->:" + i);
	}


	public static string HttpGet(string url)
	{
	    try
	    {
	        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
	        request.Method = "GET";

	        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
	        StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
	        string content = reader.ReadToEnd();
	        return content;
	    }
	    catch (Exception e)
	    {
	        return e.Message;
	    }

	}

运行结果如下:

虽然执行结果看起来很乱,但仔细比对可以发现最终的DoShortTime是按顺序执行的。

(本文完)

作者:老付 如果觉得对您有帮助,可以下方的订阅,或者选择右侧捐赠作者,如果有问题,请在捐赠后咨询,谢谢合作 如有任何知识产权、版权问题或理论错误,还请指正。 自由转载-非商用-非衍生-保持署名,请遵循:创意共享3.0许可证 交流请加群113249828:点击加群 或发我邮件 laofu_online@163.com

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏linjinhe的专栏

LevelDB:使用介绍

Get 接口和 Put 接口比较像,除了 leveldb::ReadOptions 参数是用来控制读操作的,具体见链接指向的代码。

83650
来自专栏calvin

asp.net mvc输出自定义404等错误页面,非302跳转。

朋友问到一个问题,如何输出自定义错误页面,不使用302跳转。当前页面地址不能改变.

23210
来自专栏DOTNET

.Net多线程编程—误用点分析

1 共享变量问题 错误写法: 所有的任务可能会共享同一个变量,所以输出结果可能会一样。 1 public static void Error() 2 { 3 ...

36180
来自专栏一个爱瞎折腾的程序猿

mvc一对多模型表单的快速构建

Q:在实际的开发中,经常会遇到一个模型中包含有多个条目的表单。如何将数据提交到后台? A: 以数组的形式提交到后台就Ok了(真的那么简单么,如果再嵌套一层呢?...

32320
来自专栏跟着阿笨一起玩NET

使用windows服务和MSMQ和进行日志管理(解决高并发问题)

然后就可以写我们的代码了,我们的服务需要实时监视MSMQ的队列中有没有记录,如果有,就向数据库中插入

58710
来自专栏菩提树下的杨过

Pechkin:html -> pdf 利器

Pechkin 是GitHub上的一个开源项目,可方便将html转化成pdf文档,使用也很方便,下面是winform项目中的示例代码: using System...

28150
来自专栏一只程序汪的自我修养

ASP.NET MVC 微信JS-SDK认证

31230
来自专栏丑胖侠

《Drools7.0.0.Final规则引擎教程》第4章 4.2 auto-focus

auto-focus 在agenda-group章节,我们知道想要让AgendaGroup下的规则被执行,需要在代码中显式的设置group获得焦点。而此属性可配...

22890
来自专栏知识分享

51采集PCF8591数据通过ESP8266上传C#上位机android 之TCP客户端编程ESP8266使用详解NodeMCU初探ESP8266刷AT固件与nodemcu固件ESP8266使用详解-

这两天测试程序还发现一个bug就是如果客户端断开了,应该检测一下哪个断开了,数据就不应该发向那个连接,,,否则就会报错,然后模块会复位重启 所以加上这段代码 c...

61950
来自专栏听雨堂

用Nunit测试通讯程序

    对于Nunit,我是个纯粹的新手,没想到,第一个练手的,居然是一个通讯程序。难度略微大了点。我的通讯程序是一个类似通讯服务器的程序,能够监听和维持多个连...

20870

扫码关注云+社区

领取腾讯云代金券