Task.Factory.New()创建线程的隐患:启动慢

一、消费者线程输出特别慢

        老旧的代码,现在需要加一些新玩意,为此还给他换了一台机器。老旧的代码摇摇欲坠,小心翼翼的维护,每改一行都担心受怕,就像头上悬了一把刀。老代码中有一些数据,现在需要发送给总控,所以我需要添加一个TcpServer,感觉这个不是很难。大概就是开几个线程,一个发状态,一个发数据A,一个发数据B。

        问题来了,当我加上网络的初始化之后,老代码中有一个数据在刚打开时往界面加载变慢了许多,如果不初始化我的网络模块就没问题。我陷入了沉思,根本找不到原因。

二、问题的所在

        很久很久以后,就连客户都要放弃的时候,有一个午夜,我进行了一次尝试。主线程中,先打开生产者线程,然后打开消费者线程,生产者线程将数据推入缓冲区,消费者线程开始取。

//主线程
//---记录此刻时间A;
System.Threading.Task.Factory.StartNew(()=>{
    //---记录此刻时间B;
    //开始生产数据……填入缓冲区   
});
System.Threading.Task.Factory.StartNew(()=>{
    //---记录此刻时间C;
    //从缓冲区读数据……往界面推数据
});

        我加上了记录三个时间节点A、B、C的代码,发现在最坏的情况下BC的时间比A晚了四五秒。

        为什么去掉网络的初始化就好了?

        我打开了线程界面,发现初始化网络之后线程数从15左右增加到了40左右;我意识到了问题的所在,难道Task有问题?

三、查找资料

        我去网上找,为什么Task没有立即启动!没什么人回答,毕竟CSharper还是太少,在混乱的网络里还是有一些蛛丝马迹被我发现了。有人提到Task用的是ThreadPool,而线程池是有数量限制的,数量超过之后会有问题。

四、写代码验证

        ThreadPool.GetMinThreads(out minWorkers,out minIOC);可以获取当前最小线程保持活跃数,也就是说即是没有线程也会保持minWorker个线程,这样在需要大量创建线程的时候就不会耗费太久的时间。

        这台机器默认的值是8,ThreadPool.SetMinThreads(50, 50)可以进行设置,设置之后果然好了许多,还待进一步观察。

int minWorkers, minIOC;
int maxWorkers, maxIOC;
ThreadPool.GetMinThreads(out minWorkers,out minIOC);
Console.WriteLine(minWorkers+"-min-"+minIOC);
ThreadPool.GetMaxThreads(out maxWorkers, out maxIOC);
Console.WriteLine(maxWorkers + "-max-" + maxIOC);
if (!ThreadPool.SetMinThreads(50, 50))
{
    Console.WriteLine("设置失败!");
}

        接着使用两种方法进行测试,一个使用Task开线程,一个使用Thread开线程。使用Task的前minWorkers线程时间差不多,但是之后的就开始耗时起来了,平均一个0.5到1秒不等。使用Thread的没有这个问题,会不会有别的问题呢?我还不知道!

string rootPaht = Environment.CurrentDirectory;
​
//使用Task.Factory.start开启线程
System.IO.Directory.CreateDirectory(rootPaht + "/abcdefg");
for (int i = 0; i < 20; i++)
{
    System.Threading.Tasks.Task.Factory.StartNew(() =>{
    int j = 1000;
    while (j-->0)
    {
        string s = Thread.CurrentThread.ManagedThreadId.ToString() +"--"+ Thread.CurrentThread.Name
        + "\t----------" + DateTime.Now.ToString("hhmmss:fff");
        string path = rootPaht+"/abcdefg/" + Thread.CurrentThread.ManagedThreadId.ToString()+".txt";
        System.IO.File.AppendAllLines(path, new string[] { s });
        Thread.Sleep(30);
    }
    });
}
//使用Thread开启线程
System.IO.Directory.CreateDirectory(rootPaht + "/hijklmn");
for (int i = 0; i < 20; i++)
{
    new Thread(()=> {
    int j = 1000;
    while (j-- > 0)
    {
        string s = Thread.CurrentThread.ManagedThreadId.ToString() + "--" + Thread.CurrentThread.Name
        + "\t----------" + DateTime.Now.ToString("hhmmss:fff");
        string path = rootPaht + "/hijklmn/" + Thread.CurrentThread.ManagedThreadId.ToString() + ".txt";
        System.IO.File.AppendAllLines(path, new string[] { s });
        Thread.Sleep(30);
    }
    }).Start();
}

五、修改

        测试之后,我在老代码上加上了ThreadPool.SetMinThreads(50, 50),情况好转了许多。暂时算是还可以,看看之后在哪里翻车吧!对了,老代码用的是VS2010+.NETFramework4.0,比较古老了!

        可是为什么网络模块新建了那么多线程呢,我是不是要换一个库了!不过最近的库都不支持4.0了,升级代码版本也是很头疼!

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C#中,可以使用`Task.Factory.StartNew`方法来启动一个后台线程,并使用`Invoke`方法在UI线程上更新`Chart`控件,以避免卡顿UI。以下是一个示例代码: ```csharp using System; using System.Threading.Tasks; using System.Windows.Forms; using System.Windows.Forms.DataVisualization.Charting; class Program { static void Main(string[] args) { var chart = new Chart(); chart.Series.Add("Series1"); chart.ChartAreas.Add("ChartArea1"); chart.ChartAreas[0].AxisX.Minimum = 0; chart.ChartAreas[0].AxisX.Maximum = 10; chart.ChartAreas[0].AxisY.Minimum = 0; chart.ChartAreas[0].AxisY.Maximum = 100; var form = new Form(); form.Controls.Add(chart); form.Load += (sender, e) => { Task.Factory.StartNew(() => { var random = new Random(); var i = 0; while (true) { var value = random.Next(0, 100); chart.Invoke((Action)(() => { chart.Series["Series1"].Points.AddXY(i, value); if (chart.Series["Series1"].Points.Count > 10) { chart.ChartAreas[0].AxisX.Minimum = chart.Series["Series1"].Points[chart.Series["Series1"].Points.Count - 10].XValue; chart.ChartAreas[0].AxisX.Maximum = chart.Series["Series1"].Points[chart.Series["Series1"].Points.Count - 1].XValue; } })); Task.Delay(1000).Wait(); // 每隔1秒钟执行一次循环 i++; } }, TaskCreationOptions.LongRunning); }; Application.Run(form); } } ``` 在这个示例中,我们创建了一个`Chart`控件,并在`Form`中显示它。在`Form`的`Load`事件处理程序中,我们使用`Task.Factory.StartNew`方法来启动一个后台线程,并在其中执行循环。在循环中,我们使用`Invoke`方法来在UI线程上更新`Chart`控件。我们还检查了`Chart`中点的数量,以便在添加新点后自动调整`Chart`的`AxisX`范围,以保持最近的10个点可见。最后,我们使用`Application.Run`方法来运行`Form`并显示`Chart`控件。 请注意,在使用`Invoke`方法时,必须将更新UI的代码封装在一个`Action`委托中,以便它可以在UI线程上执行。此外,我们使用了`TaskCreationOptions.LongRunning`选项来告诉.NET运行时,该任务将运行较长时间,并可能在后台线程上使用较多的资源。最后,我们在`Task.Delay`后面调用了`.Wait()`方法来确保在执行下一次循环之前等待1秒钟,以避免在后台线程上卡顿UI。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值