簡體   English   中英

C#Generic List foreach OutofMemoryException

[英]C# Generic List foreach OutofMemoryException

我有一個程序,從數據庫中讀取大約200萬行到List。 每行是包含地理坐標等信息的位置。

將數據添加到List后,我使用foreach循環並獲取坐標以創建kml文件。 當行數很大時,循環遇到OutOfMemoryException錯誤(但是否則完美地工作)。

有關如何處理此問題的任何建議,以便程序可以處理非常大的數據集? kml庫是SharpKML。

我還是C#的新手,所以請放輕松!

這是循環:

            using (SqlConnection conn = new SqlConnection(connstring))
        {
            conn.Open();
            SqlCommand cmd = new SqlCommand(select, conn);

            using (cmd)
            {
                SqlDataReader reader = cmd.ExecuteReader();
                while (reader.Read())
                {
                    double lat = reader.GetDouble(1);
                    double lon = reader.GetDouble(2);
                    string country = reader.GetString(3);
                    string county = reader.GetString(4);
                    double TIV = reader.GetDouble(5);
                    double cnpshare = reader.GetDouble(6);
                    double locshare = reader.GetDouble(7);

                    //Add results to list
                    results.Add(new data(lat, lon, country, county, TIV, cnpshare, locshare));
                }
                reader.Close();
            }
            conn.Close();
        }

            int count = results.Count();
            Console.WriteLine("number of rows in results = " + count.ToString());

            //This code segment generates the kml point plot

            Document doc = new Document();
            try
            {
                foreach (data l in results)
                {
                    Point point = new Point();
                    point.Coordinate = new Vector(l.lat, l.lon);

                    Placemark placemark = new Placemark();
                    placemark.Geometry = point;
                    placemark.Name = Convert.ToString(l.tiv);

                    doc.AddFeature(placemark);

                }
            }
            catch(OutOfMemoryException e)
            {
                throw e;
            }

這是列表中使用的類

        public class data
    {
        public double lat { get; set; }
        public double lon { get; set; }
        public string country { get; set; }
        public string county { get; set; }
        public double tiv { get; set; }
        public double cnpshare { get; set; }
        public double locshare { get; set; }

        public data(double lat, double lon, string country, string county, double tiv, double cnpshare,
            double locshare)
        {
            this.lat = lat;
            this.lon = lon;
            this.country = country;
            this.county = county;
            this.tiv = tiv;
            this.cnpshare = cnpshare;
            this.locshare = locshare;
        }

    }

為什么在編寫之前需要存儲所有數據? 不是將每一行添加到列表中,而是應該在讀取時處理每一行,然后忘記它。

例如,嘗試將代碼一起滾動如下:

Document doc = new Document();
while (reader.Read())
{
    // read from db
    double lat = reader.GetDouble(1);
    double lon = reader.GetDouble(2);
    string country = reader.GetString(3);
    string county = reader.GetString(4);
    double TIV = reader.GetDouble(5);
    double cnpshare = reader.GetDouble(6);
    double locshare = reader.GetDouble(7);

    var currentData = new data(lat, lon, country, county, TIV, cnpshare, locshare));

    // write to file
    Point point = new Point();
    point.Coordinate = new Vector(currentData.lat, currentData.lon);

    Placemark placemark = new Placemark();
    placemark.Geometry = point;
    placemark.Name = Convert.ToString(currentData.tiv);

    doc.AddFeature(placemark);
}

這只有在Document合理實施的情況下才有效。

奧利弗是對的(從我這里投票)。 性能方面你可以做一些其他的事情。 首先不要查詢您不會使用的字段。 然后在while語句(?)之前移動所有變量聲明(Oliver的代碼)。 最后,不是等待你的sql server收集並發回所有記錄,而是逐步執行步驟。 例如,如果您的記錄具有UID,並且獲取它們的順序是此UID,則從本地C#變量“var lastID = 0”開始,將您的select語句更改為類似(預格式化)“select top 1000 ...其中UID> lastID“並重復您的查詢,直到您什么都沒有或任何東西少於1000條記錄。

如果使用數據庫中的數據填充列表沒有大的延遲,並且您沒有提到使用數據填充列表時出現問題,為什么不立即創建Point和Placemark對象。 代碼如下。

    var doc = new Document();

    using (SqlConnection conn = new SqlConnection(connstring))
    {
        conn.Open();
        SqlCommand cmd = new SqlCommand(select, conn);

        using (cmd)
        {
            var reader = cmd.ExecuteReader();
            while (reader.Read())
            {
                double lat = reader.GetDouble(1);
                double lon = reader.GetDouble(2);
                string country = reader.GetString(3);
                string county = reader.GetString(4);
                double TIV = reader.GetDouble(5);
                double cnpshare = reader.GetDouble(6);
                double locshare = reader.GetDouble(7);

                var point = new Point();
                point.Coordinate = new Vector(lat , lon );

                var placemark = new Placemark();
                placemark.Geometry = point;
                placemark.Name = Convert.ToString(TIV);

                doc.AddFeature(placemark);

            reader.Close();
        }
        conn.Close();
    }

如果沒有充分的理由在內存中檢索如此多的數據,請嘗試使用一些延遲加載方法。

@drdigit,

我會避免在循環中執行查詢。 一個查詢應始終返回該時刻所需的數據。 在這種情況下,您將有1000個返回1000行的查詢。 也許最好快速顯示前1000行,但我不確定如果你在循環中執行1000個更快的查詢而不是只執行一個查詢它會更快。也許我錯了....

我認為如果在這種情況下需要,你的方法對延遲加載很有用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM