繁体   English   中英

C#:从foreach循环每5分钟调用一次方法

[英]C# : Call a method every 5 minutes from a foreach loop

我的控制台应用程序正在从文本文件中读取大量数据,这些数据将被保存到数据库中。 为此,我将数据存储到DataTable中,并且希望每5分钟将此数据表转储到DB中(如果我想一次转储整个数据,则必须用整个数据集填充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();
}

我在这里想要的是,代码将继续填充DataTable,并且每隔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();
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM