C# :每 5 分钟从 foreach 循环调用一次方法
本文关键字:方法 一次 调用 循环 分钟 foreach | 更新日期: 2023-09-27 18:04:26
我的控制台应用程序正在从文本文件中读取大量数据,这些数据将保存到数据库中。为此,我将数据存储到一个 DataTable 中,并且我想每 5 分钟将此 DataTable 转储到数据库(如果我想一次转储整个数据,那么我必须用整组数据填充 DataTable,在这种情况下,我得到了 OutOfMemoryException(。
public void ProcessData()
{
string[] files=File.ReadAllLines(path)
foreach(var item in files)
{
DataRow dtRow= dataTable.NewRow();
dtRow["ID"]= .... //some code here;
dtRow["Name"]= .... //some code here;
dtRow["Age"]= .... //some code here;
var timer = new Timer(v => SaveData(), null, 0, 5*60*1000);
}
}
public void SaveData(string tableName, DataTable dataTable )
{
//Some code Here
//After dumping data to DB, clear DataTable
dataTable.Rows.Clear();
}
我在这里想要的是,代码将继续填充数据表,并且每 5 分钟它将调用 SaveData(( 方法。这将继续运行,直到处理完所有文件。
但是,我已经看到,当调用 SaveData(( 方法时,它会执行 4-5 次。有时,它每 5 分钟调用一次机器人。
我不知道如何在这里进行。如何解决这个问题?这里可以使用任何其他方法吗? 任何帮助,不胜感激。
是否必须使用 ReadAllLines 完全读取每个文本文件,这将消耗大量内存。为什么不从文件中读取 x 行,保存到数据库,然后继续直到到达文件末尾?
你最大的问题是在你的 foreach 中实例化新的Timer
实例。 每个 foreach 调用中的新Timer
对象意味着多个线程同时调用SaveData
,这意味着dataTable
同时处理并保存到数据库中,可能(并且可能(在清除行之前,从而将大部分文件复制到数据库中。
在我为所提出的问题提供解决方案之前,我想指出,以 5 分钟的间隔保存数据具有明显的代码气味。 正如已经指出的,我建议使用某种方法,根据某些数据大小而不是任意时间间隔加载和保存数据。 也就是说,我将继续回答您的问题,假设您必须以 5 分钟的间隔保存是有原因的。
首先,我们需要正确设置我们的Timer
,您会注意到我在 foreach 循环之外创建的。 Timer
继续按时间间隔运行,而不仅仅是等待和执行一次。
其次,我们必须采取措施确保中间数据存储上的线程安全数据完整性(在您的情况下,您使用了DataTable
,但我使用的是自定义类的List
,因为DataTable
对于我们想要做的事情来说成本太高(。 您会注意到我通过在更新我们的List
之前锁定来实现这一点。
数据处理类的更新:
private bool isComplete = false;
private object DataStoreLock = new object();
private List<MyCustomClass> myDataStore;
private Timer myTimer;
public void ProcessData()
{
myTimer = new Timer(SaveData, null, TimeSpan.Zero, TimeSpan.FromMinutes(5.0));
foreach (var item in File.ReadLines(path))
{
var myData = new MyCustomClass()
{
ID = 0, // Some code here
Name = "Some code here",
Age = 0 // Some code here
};
lock (DataStoreLock)
{
myDataStore.Add(myData);
}
}
isComplete = true;
}
public void SaveData(object arg)
{
// Our first step is to check if timed work is done.
if (isComplete)
{
myTimer.Dispose();
myTimer = null;
}
// Our next step is to create a local instance of the data store to work on, which
// allows ProcessData to continue populating while our DB actions are being performed.
List<MyCustomClass> lDataStore;
lock (DataStoreLock)
{
lDataStore = myDataStore;
myDataStore = new List<MyCustomClass>();
}
//Some code DB code here.
}
编辑:我已将枚举更改为通过ReadLines
而不是ReadAllLines
。阅读 MSDN 上ReadLines
方法下的备注。 ReadAllLines
将是一个阻塞调用,而ReadLines
将允许在读取文件时处理枚举。我无法想象如果文件已经全部读取到内存中,您的foreach
将运行超过 5 分钟的情况。
以下是有关如何实现代码的建议以及来自其他答案的建议:
public void ProcessData()
{
int i = 1;
foreach(var item in File.ReadLines(path)) //This line has been edited
{
DataRow dtRow= dataTable.NewRow();
dtRow["ID"]= .... //some code here;
dtRow["Name"]= .... //some code here;
dtRow["Age"]= .... //some code here;
if (i%25 == 0) //you can change the 25 here to something else
{
SaveData(/* table name */, /* dataTable */);
}
i++;
}
SaveData(/* table name */, /* dataTable */);
}
public void SaveData(string tableName, DataTable dataTable )
{
//Some code Here
//After dumping data to DB, clear DataTable
dataTable.Rows.Clear();
}