C # Процесс приложения зависает через некоторое время - PullRequest
2 голосов
/ 07 апреля 2010

Я реализовал простое приложение на C #, которое вставляет в базу данных около 350000 записей. Раньше это работало хорошо, и процесс занимал примерно 20 минут.

Я создал индикатор выполнения, который позволяет приблизительно узнать ход вставки записей. Когда индикатор выполнения достигает примерно 75%, он перестает прогрессировать. Я должен вручную завершить программу, поскольку процесс, кажется, не завершается. Если я использую меньше данных (например, 10000), индикатор выполнения завершается, и процесс завершается. Однако, когда я пытаюсь вставить все записи, этого больше не произойдет.

Обратите внимание, что если я подожду дольше, чтобы завершить программу вручную, было бы добавлено больше записей. Например, если я завершаю программу через 15 минут, вставляется 200000 записей, тогда как если я завершаю программу через 20 минут, вставляются 250000 записей.

Эта программа использует один поток. По лицу я больше ничего не могу сделать, пока процесс не будет завершен. Это как-то связано с потоками или процессами?

Любая обратная связь будет принята с благодарностью.

Спасибо.

Ответы [ 9 ]

9 голосов
/ 07 апреля 2010

Удивительно, что ваш индикатор прогресса работает вообще.Если вы не используете отдельный поток, тогда ваша длительная задача остановит цикл обработки сообщений, что приведет к тому, что ваше приложение не отвечает.

Вы должны запустить эту задачу, используя BackgroundWorker .Поместите свой долгосрочный код в обработчик для события DoWork .Используйте ReportProgess для обновления индикатора выполнения.Не обращайтесь к элементам управления формы непосредственно из обработчика DoWork.

Есть несколько примеров того, как это сделать в MSDN.

Также убедитесь, что вы не обновляете индикатор выполнения.за каждое изменение.Если у вас есть 100 000 записей, обновляйте, например, индикатор выполнения для каждых 100 или 1000 записей.Слишком много событий может также привести к тому, что программа перестает отвечать на запросы.

2 голосов
/ 07 апреля 2010

Если вы вставите много записей, попробуйте использовать массовое копирование. Это значительно повысит скорость вашего приложения. Эти функции довольно просты: вы помещаете все свои записи для вставки в таблицу данных (с той же схемой, что и таблица назначения) и вызываете функцию вместе с ней.

Чтобы получить схему данных, если вам лень, просто сделайте запрос типа «SELECT * FROM tableName WHERE 0 = 1», результирующий набор будет содержать только схему имени таблицы.

    private static void InsertTable(DataTable dt)
    {
        dt.AcceptChanges();

        using (SqlBulkCopy bulkCopy = new SqlBulkCopy(System.Configuration.ConfigurationManager.ConnectionStrings["MyDB"].ToString()))
            {
                //Destination Table is the same as the source.
                bulkCopy.DestinationTableName = dt.TableName;
                try
                {
                    // Write from the source to the destination.
                    bulkCopy.BulkCopyTimeout = 600;
                    bulkCopy.WriteToServer(dt);
                } 
                catch (Exception ex)
                {
                    Console.Write(ex.Message);
                }
            }


    }
    private static void InsertTableWithIdentity(DataTable dt)
    {
        dt.AcceptChanges();

        using (SqlBulkCopy bulkCopy = new SqlBulkCopy(System.Configuration.ConfigurationManager.ConnectionStrings["MyDB"].ToString(), SqlBulkCopyOptions.KeepIdentity))
        {
            //Destination Table is the same as the source.
            bulkCopy.DestinationTableName = dt.TableName;
            try
            {
                // Write from the source to the destination.
                bulkCopy.BulkCopyTimeout = 600;
                bulkCopy.WriteToServer(dt);
            }
            catch (Exception ex)
            {
                Console.Write(ex.Message);
            }
        }
    }

Что касается того, почему он замедляется, то все просто, время, необходимое для выполнения запроса, увеличивается экспоненциально с количеством записей. Поскольку он хранит в памяти будущее состояние базы данных и записывает его только после коммита (в вашем случае конец транзакции), поэтому используйте массовую копию, просто поместив еще несколько коммитов.

0 голосов
/ 13 апреля 2010

В конце концов, проблем не было. Проблема заключалась в том, что я использовал виртуальную машину, и поэтому она была немного медленной. Когда я запустил это на сервере Xeon, процесс завершился примерно за 10 минут.

0 голосов
/ 08 апреля 2010

Я использую темы для такого рода вещей.

где-то в моем коде:

// Definition 
private static Thread TH; 

....

// When process starts
TH = new Thread(new ThreadStart(Splash_MyCallBack)); 
TH.Start();

....

// This method starts the form that shows progress and other data
static private void Splash_MyCallBack()
{
   frmLoading FL;

   FL = new frmLoading();

   FL.ShowDialog();

} /* Splash_MyCallBack*/

// Your process calls Splash_Stop when it is done.
static public void Splash_Stop()
{
   TH.Abort();
} /* Splash_Stop*/

frmLoading выполняет визуальные функции, в то время как в фоновом режиме у меня очень трудоемкая задача.Мой процесс сообщает интерфейсу о своем прогрессе.frmLoading реализует этот интерфейс, так что он знает о нем и может показывать, когда это необходимо (2 индикатора выполнения в моем случае). Единственное, что нужно, это значит, что frmLoading должен иметь это в конструкторе:рискованно в некоторых сценариях (не мой случай).

Надеюсь, это поможет, я могу добавить больше материала, если хотите.

С уважением,

0 голосов
/ 07 апреля 2010

Сначала создайте функцию, которая изменяет индикатор выполнения, затем добавьте делегата для этой функции. Создайте другой поток, чтобы обновить индикатор выполнения. Когда закончите, это должно выглядеть так.

private delegate void UpdateProgressBarDelegate();

private void UpdateProgressBar()
{
     if (this.progressBar1.InvokeRequired)
     {
         this.progressBar1.Invoke(new UpdateProgressBarDelegate(UpdateProgressBar));
     }
     else
     {
         //code to update progress bar
     }    
}

Если вам нужно включить какие-либо параметры, вы должны сделать это так:

this.progressBar1.Invoke(new UpdateProgressBarDelegate(UpdateProgressBar), param1, param2);
0 голосов
/ 07 апреля 2010

Факты:

  • вы заявляете, что оно зависает, в то время как через несколько строк вы заявляете, что приложение все еще что-то обрабатывает, и чем позже вы завершаете его, тем больше элементов фактически обрабатывается до его уничтожения.

Нелегко сказать, в чем проблема, без какого-либо исходного кода, но я бы заподозрил замедление из-за утечки памяти или другого фактора снижения производительности.

Несколько догадок:

  • Закрываете ли вы все ненужные подключения к базе данных? Нераспределенные соединения с базой данных могут вызвать огромные утечки памяти и привести к зависанию приложения.

  • Вы пытались запустить приложение в профилировщике памяти / производительности? (ANTS это здорово)

  • Пробовали ли вы через некоторое время подключить к приложению отладчик, чтобы увидеть, где именно он висит и висит ли он вообще?

0 голосов
/ 07 апреля 2010

Не относится к прогрессу, но вы вносите партии вставок? Это может значительно ускорить процесс (и сократить потребление ресурсов).

0 голосов
/ 07 апреля 2010

Вы должны запустить вставку в отдельном потоке с возможностью отменить операцию (вместо принудительного завершения). Как предложено @Mark, используйте что-то вроде BackgroundWorker или просто создайте отдельный поток и запишите это. Кажется, что в какой-то момент процесс является узким местом, возможно, вам следует заняться ведением журнала.

0 голосов
/ 07 апреля 2010

Как вы относитесь к исключениям в процессе вставки?

Какие данные вы вводите? Может ли это генерировать исключение?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...