我写了winform应用, 环境是 windows10, .NET4.6.1.
我添加了一个按钮用于读取硬盘上的文件,并且定义了Task.Run用子线程来读取文件,代码如下:
private async void ReadFileFromHardDisk_Click(object sender, EventArgs e)
{
var path = Application.StartupPath + $"\\Recipe\\{currentUnit}";
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
OpenFileDialog dialog = new OpenFileDialog();
dialog.InitialDirectory = path;
dialog.Filter = "(*.recipe)|*.recipe";
if (dialog.ShowDialog() == DialogResult.OK)
{
string fileName = @dialog.FileName;
DataForm dataForm = await UtilsSerialize.FileToDataAsync<DataForm>(fileName);
ShowDataFormToUI(dataForm);
}
}
public class UtilsSerialize
{
public static Task<T> FileToDataAsync<T>(string fileName)
{
WriteLog($"{DateTime.Now:HH:mm:ss} FileToDataAsync<T>({fileName}) ThreadId:
{Thread.CurrentThread.ManagedThreadId}\r\n");
return Task.Run<T>(() =>
{
WriteLog($"{DateTime.Now:HH:mm:ss} FileToDataAsync->Task.Run Start {fileName} ThreadId:
{Thread.CurrentThread.ManagedThreadId}\r\n");
try
{
using (FileStream fs = new FileStream(fileName, FileMode.Open))
{
using (TextReader textReader = new StreamReader(fs, Encoding.Default))
{
JsonSerializer serializer = new JsonSerializer();
T res = (T)serializer.Deserialize(textReader, typeof(T));
return res;
}
}
}
catch (Exception ex)
{
return default(T);
}
finally
{
WriteLog($"{DateTime.Now:HH:mm:ss} FileToDataAsync->Task.Run End {fileName} ThreadId:
{Thread.CurrentThread.ManagedThreadId}\r\n");
}
}
}
}
一开始运行好好的,Task.Run马上就执行了,但是代码跑了一段时间后,问题就来了,日志记录如下:
20:47:09 FileToDataAsync(C:\Users\iei\Desktop\APP\Recipe\3\0315.recipe) ThreadId:1
20:52:07 FileToDataAsync->Task.Run Start C:\Users\iei\Desktop\APP\Recipe\3\0315.recipe ThreadId:4
20:52:07 FileToDataAsync->Task.Run End C:\Users\iei\Desktop\APP\Recipe\3\0315.recipe ThreadId:4
20:50:16 DataToFileAsync(C:\Users\iei\Desktop\APP\Recipe\6\High.recipe) ThreadId:1
20:53:42 DataToFileAsync->Task.Run Start C:\Users\iei\Desktop\APP\Recipe\6\High.recipe ThreadId:17
20:53:42 DataToFileAsync->Task.Run End C:\Users\iei\Desktop\APP\Recipe\6\High.recipe ThreadId:17
20:53:11 FileToDataAsync(C:\Users\iei\Desktop\APP\Recipe\2\0315.recipe) ThreadId:1
20:55:12 FileToDataAsync->Task.Run Start C:\Users\iei\Desktop\APP\Recipe\2\0315.recipe ThreadId:30
20:55:12 FileToDataAsync->Task.Run End C:\Users\iei\Desktop\APP\Recipe\2\0315.recipe ThreadId:30
Task.Run这下要几分钟之后才能开始了. 只有重启winform应用,Task.Run才能恢复到立马开始,这是为什么呢,感谢帮助!
我猜啊,Task.Run本质上应该是线程池,如果当前线程池在忙(就是所有线程都在用),那么新的任务就会等待,等到空闲的线程。
return await Task.Run(() => ,你这样写就可以了,再把方法加上修饰符async,这杨就不会出现这种情况了
Task.Run得不到线程池分配的线程,这个现象叫thread pool starvation。
简单处理的话有两种思路,一是利用ThreadPool.SetMinThreads方法提高最小并发线程数量。二是长期运行(比如与应用生命周期相同)的Task,用Task.Factory.StartNew方法创建时,设置为TaskCreationOptions.LongRunning。
进一步处理的话,可以自己继承TaskScheduler,自己来控制线程池执行任务。详细参见这位高手的系列文章,https://www.cnblogs.com/s0611163/p/6178973.html
1.服务器的资源利用情况如何?
2.是否设置了线程池的上限(设置过小。)改大一点
3.async await 要链式调用,FileToDataAsync 建议改成 async await模式。(不清楚问题是否和这个有关系)
去掉静态方法,改成实例调用呢?
楼主找到原因了吗
原因见thread pool starvation那条评论