C# 线程完成任务后释放内存
将断点放在C# 线程完成任务后释放内存,c#,C#,将断点放在线程启动之前,您会注意到控制台消耗了大约5到8MB的内存,但一旦线程启动,它的内存将达到17到20MB。在关闭控制台之前,此内存将一直使用。在线程完成it任务后,如何释放内存?有更好的解决办法吗 现在的问题是:为什么我需要它,因为垃圾收集器会在需要时自动释放内存。我需要它,因为我正在做网页抓取,我有一个全局类来存储所有抓取的html文本,我必须抓取大约10k页,并将html存储到全局类。发生的事情是:当我在将500个html数据刮到global class后运行这个应用程序时,它几乎吃
线程
启动之前,您会注意到控制台消耗了大约5到8MB的内存,但一旦线程
启动,它的内存将达到17到20MB。在关闭控制台之前,此内存将一直使用。在线程
完成it任务后,如何释放内存?有更好的解决办法吗
现在的问题是:为什么我需要它,因为垃圾收集器会在需要时自动释放内存。我需要它,因为我正在做网页抓取,我有一个全局类来存储所有抓取的html文本,我必须抓取大约10k页,并将html存储到全局类。发生的事情是:当我在将500个html数据刮到global class后运行这个应用程序时,它几乎吃掉了我电脑内存的100%,即20GB。所以我需要释放内存。我不能关闭控制台应用程序来释放内存bcoz我有一些计算后,收集所有的html
class DemoData
{
public int Id { get; set; }
public string Text { get; set; }
public static List<DemoData> data = new List<DemoData>();
}
class Program
{
public static void Main()
{
for (var i = 0; i < 5000; i++)
{
DemoData.data.Add(new DemoData
{
Id = i,
Text = "something....",
});
}
foreach (var item in DemoData.data)
{
var t = new Thread(new ThreadStart(DoSomething));//put break point here and see.
t.Name = item.Id.ToString(); ;
t.Start();
}
Console.WriteLine("wait");
Console.ReadLine();
}
public static void DoSomething()
{
Thread thr = Thread.CurrentThread;
Console.WriteLine(thr.Name);
}
}
类解调器
{
公共int Id{get;set;}
公共字符串文本{get;set;}
公共静态列表数据=新列表();
}
班级计划
{
公共静态void Main()
{
对于(变量i=0;i<5000;i++)
{
解调数据。数据。添加(新解调数据
{
Id=i,
Text=“某物…”,
});
}
foreach(DemoData.data中的变量项)
{
var t=new Thread(new ThreadStart(DoSomething));//将断点放在这里并查看。
t、 Name=item.Id.ToString();
t、 Start();
}
控制台。写入线(“等待”);
Console.ReadLine();
}
公共静态无效剂量测定法()
{
Thread thr=Thread.CurrentThread;
控制台写入线(名称);
}
}
您只需等待垃圾收集器运行,您当前的应用程序不够复杂,不需要运行垃圾收集器,除非关闭,否则您不会看到这种情况发生 因此,框架会在感觉需要时触发GC,然后查看调用堆栈,确定不再需要的对象并删除它们,释放内存,这将完全自动发生,而无需执行任何操作 如果你想帮助GC解决问题,你可以检查你的变量范围,这样GC就可以看到它不再需要了,因为它完全超出了范围,所以避免全局变量,不在不需要的数据对象之间创建链接,在值和引用类型之间做出正确的选择,等等 但是,如果您最终需要手动启动GC,则可以调用GC.Collect() 看见
您可能需要使用内存探查器来检查潜在的内存泄漏 问题的一个可能原因是线程数量太多,没有理由使用比cpu内核更多的线程。使用的每个线程都需要一些内存来进行堆栈和其他内部管理
一个相当简单的方法是将需要访问的地址放入集合中,并使用。这将尝试调整用于最大化吞吐量的线程数。更复杂的变体可以使用一个并发集合和多个使用者。还有许多IO调用的异步变体,以避免在等待IO时阻塞线程的内存开销。我建议您阅读一些多生产者/消费者模式的示例以了解更多详细信息。如果您的问题是将数据馈送到线程中进行处理,那么我建议您看看这一点,它允许您创建线程安全的处理缓冲区 这里是一个工作示例,注意这是使用.NET5语法
using System;
using System.Threading.Channels;
using System.Net.Http;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
var sites = Enumerable.Range(0, 5000).Select(i => @"http:\\www.example.com");
//create thread safe buffer no limit on size
var sitebuffer = Channel.CreateUnbounded<string>();
//create thread safe buffer limited to 10 elements
var htmlbuffer = Channel.CreateBounded<string>(10);
async Task Feed()
{
//while the buffer hasn't closed, wait for new data to be available
while(await sitebuffer.Reader.WaitToReadAsync())
{
//read the next available url from the buffer
var uri = await sitebuffer.Reader.ReadAsync();
var http = new HttpClient();
var html= await http.GetAsync(uri);
Console.WriteLine("reading site");
//load the return text to the htmlbuffer, if buffer is full wait for space
await htmlbuffer.Writer.WriteAsync(await html.Content.ReadAsStringAsync());
Console.WriteLine("reading site complete");
}
}
async Task Process()
{
//while the buffer hasn't closed, wait for new data to be available
while (await htmlbuffer.Reader.WaitToReadAsync())
{
//read html from buffer send to doSomething then read next element
var html = await htmlbuffer.Reader.ReadAsync();
await doSomethingWithHTML(html);
}
}
async Task doSomethingWithHTML(string html)
{
await Task.Delay(2);
Console.WriteLine("done something");
}
//start 4 feeders threads
var feeders = new[]
{
Feed(),
Feed(),
Feed(),
Feed(),
};
//start 2 worker threads
var workers = new[]
{
Process(),
Process(),
};
//start of feeding in sites
foreach (var item in sites)
{
await sitebuffer.Writer.WriteAsync(item);
}
//mark that all sites have been fed into the systems
sitebuffer.Writer.Complete();
//wait for all feeders to finish
await Task.WhenAll(feeders);
//mark that no more sites will be read
htmlbuffer.Writer.Complete();
//wait for all workers to finish
await Task.WhenAll(workers);
Console.WriteLine("all tasks complete");
使用系统;
使用System.Threading.Channel;
使用System.Net.Http;
使用System.Collections.Generic;
使用System.Threading.Tasks;
使用System.Linq;
var sites=Enumerable.Range(0,5000)。选择(i=>@“http:\\www.example.com”);
//创建线程安全缓冲区大小没有限制
var sitebuffer=Channel.CreateUnbounded();
//创建限制为10个元素的线程安全缓冲区
var htmlbuffer=Channel.CreateBounded(10);
异步任务提要()
{
//当缓冲区尚未关闭时,等待新数据可用
while(等待sitebuffer.Reader.WaitToReadSync())
{
//从缓冲区读取下一个可用url
var uri=await sitebuffer.Reader.ReadAsync();
var http=new-HttpClient();
var html=await http.GetAsync(uri);
Console.WriteLine(“阅读网站”);
//如果缓冲区已满,请将返回文本加载到htmlbuffer,并等待空间
wait htmlbuffer.Writer.WriteAsync(wait html.Content.ReadAsStringAsync());
Console.WriteLine(“阅读站点完成”);
}
}
异步任务进程()
{
//当缓冲区尚未关闭时,等待新数据可用
while(等待htmlbuffer.Reader.WaitToReadAsync())
{
//从缓冲区读取html发送到doSomething,然后读取下一个元素
var html=await htmlbuffer.Reader.ReadAsync();
等待doSomethingWithHTML(html);
}
}
异步任务doSomethingWithHTML(字符串html)
{
等待任务。延迟(2);
Console.WriteLine(“做了某事”);
}
//启动4个进料器螺纹
无功馈线=新[]
{
Feed(),
Feed(),
Feed(),
Feed(),
};
//启动2个工作线程
var工人=新[]
{
进程(),
进程(),
};
//开始在现场喂食
foreach(站点中的var项目)
{
等待sitebuffer.Writer.WriteAsync(项);
}
//标记所有站点都已输入系统
sitebuffer.Writer.Complete();
//等待所有进料器完成
等待任务。WhenAll(喂食者);
//标记不再读取其他站点
htmlbuffer.Writer.Complete();
//等待所有工人完成
等待任务。何时(工人);
Console.WriteLine(“所有任务完成”);
注意异步任务
和等待
这是一个围绕线程的较新包装器