События или Делегаты

Язык C#: программирование на C#, портирование кода C# на VB и VB на C#.

Модератор: Ramzes

Dmitriy2003
Постоялец
Постоялец
 
Сообщения: 690
Зарегистрирован: 27.05.2003 (Вт) 22:47
Откуда: Deutschland

События или Делегаты

Сообщение Dmitriy2003 » 28.08.2008 (Чт) 22:44

Приветсвую желающих помочь!
Вопрос довольно наивный - как правильно построить логику программы ?

Есть приложение - графическая оболочка native dll, суть общатьяся с пользователем через GUI. Данные - есть совокупность строк - которую пытаюсь вывести в таблицу на основе ListView. Данных может быть много(10000 строк сденее значение). При однопоточном выполнении соответсвенно GUI виснет до момента полного запонения - что неприемлемо. Использовал генерацию событий из внешнего фонового потока - вроде бы все ничего - но время от времени вылеает странная ошибка - строка состояния (вывожу информацию о текущем состоянии процесса) генерирует необробатываемое исключение(Индекс не должен быть меньше или равен нулю). пробовал увеличить шаг до 128 строк - вроде ошибки нет, но все равно неприятно думать что это решение проблемы(шаг в 16 строк генерирует исключение постоянно), Далее пробовал вызывать процедуру заполнения таблицы и вывода информации в строку статуса асинхронно, через пул потоков и делегаты - ошибки нет, зато появилась другая проблема - не получается корректно остановить поток.

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

Как - же быть есть ли идеи ?

Малость подробностей
Изображение
вот так программа работает по логике событий, поначалу вроде все хорошо а потом
Изображение
Последний раз редактировалось Dmitriy2003 06.09.2008 (Сб) 19:56, всего редактировалось 1 раз.

Viper
Артефакт VBStreets
Артефакт VBStreets
Аватара пользователя
 
Сообщения: 4394
Зарегистрирован: 12.04.2005 (Вт) 17:50
Откуда: Н.Новгород

Re: События или Делегаты

Сообщение Viper » 29.08.2008 (Пт) 7:29

Если я ниче с утра не путаю, то тебе поможет либо класс BackgroundWorker, либо общение с GUI через метод Invoke.
Весь мир матрица, а мы в нем потоки байтов!

Sebas
Неуловимый Джо
Неуловимый Джо
Аватара пользователя
 
Сообщения: 3626
Зарегистрирован: 12.02.2002 (Вт) 17:25
Откуда: столько наглости такие вопросы задавать

Re: События или Делегаты

Сообщение Sebas » 29.08.2008 (Пт) 9:30

вероятно, нет синхронизации общих объектов.

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

sebas<-@->mail.ru

Dmitriy2003
Постоялец
Постоялец
 
Сообщения: 690
Зарегистрирован: 27.05.2003 (Вт) 22:47
Откуда: Deutschland

Re: События или Делегаты

Сообщение Dmitriy2003 » 06.09.2008 (Сб) 20:04

Вероятно Sebas - прав и синхронизация отсутвует, к сожалению я не готов выложить оригинальный код, зато я написал пример частично повторяющий логику, ну и проблемы у него такие-же. Я не стану приводить здесь код построенный по логике "Событий" т.к. он работает, хотя только отчасти "корректно".

Код: Выделить всё
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\csc.exe /target:winexe /out:app.exe /warn:0 /nologo /debug *.cs

Класс - 1
Код: Выделить всё
using System;
using System.Collections.Generic;
using System.Text;

namespace WindowsApplication2
{
    public class Person
    {
        private string name;
        private string lastName;
        private string title;
        private string adress;
        private string postalCode;
        private string city;
       
        public Person() { }
        public Person(string name)
            : this(name, null, null, null, null, null)
        {
            //
        }
        public Person(string name, string lastName)
            : this(name, lastName, null, null, null, null)
        {
            //
        }
        public Person(string name, string lastName, string title)
            : this(name, lastName, title, null, null, null)
        {
            //
        }
        public Person(string name, string lastName, string title, string adress)
            : this(name, lastName, title, adress, null, null)
        {
            //
        }
        public Person(string name, string lastName, string title, string adress, string postalCode)
            : this(name, lastName, title, adress, postalCode, null)
        {
            //
        }
        public Person(string name, string lastName, string title, string adress, string postalCode,
            string city)
        {
            this.name = name;
            this.lastName = lastName;
            this.title = title;
            this.adress = adress;
            this.postalCode = postalCode;
            this.city = city;
        }

        public string Name
        {
            get { return name; } set { name = value; }
        }
        public string LastName
        {
            get { return lastName; } set { lastName = value; }
        }
        public string Title
        {
            get { return title; } set { title = value; }
        }
        public string Adress
        {
            get { return adress; } set { adress = value; }
        }
        public string PostalCode
        {
            get { return postalCode; } set { postalCode = value; }
        }
        public string City
        {
            get { return city; } set { city = value; }
        }

        public override string ToString()
        {
            return title + ", " + name + ", " + lastName;
        }
    }
}

Класс-2
Код: Выделить всё
using System;
using System.Collections.Generic;
using System.Threading;   

namespace WindowsApplication2
{
    public delegate void CreatePersonsProgreessDelegate(Person p);
    public delegate void CreatePersonsCompleteDelegate(List<Person> persons);
    public delegate void CreatePersonsErrorDelegate (Exception ex);

    // Предположим что этот класс - есть враппер внешнего неуправляемого
    // ресурса, к примеру 'С++ native library'
    public class Persons : IDisposable
    {
        private delegate bool CPLDeleagte();

        private CreatePersonsProgreessDelegate OnProgressProc;
        private CreatePersonsCompleteDelegate OnCompleteProc;
        private CreatePersonsErrorDelegate OnErrorProc;
        private List<Person> persons;
        private AutoResetEvent pStepEvent = new AutoResetEvent(false);
        private bool abort = false;
        private bool isDisposed = false;

        ~Persons()
        {
            this.Dispose(false); 
        }

        public Persons(int capacity) : this(capacity, null, null, null) { }
        public Persons(int capacity, CreatePersonsProgreessDelegate OnProgressProc,
            CreatePersonsCompleteDelegate OnCompleteProc, CreatePersonsErrorDelegate OnErrorProc)
        {
            this.persons = new List<Person>(capacity);
            this.OnProgressProc = OnProgressProc;
            this.OnCompleteProc = OnCompleteProc;
            this.OnErrorProc = OnErrorProc;
        }

        public List<Person> PersonsList
        {
            get { return persons; }
        }
        public CreatePersonsProgreessDelegate ProgressProc
        {
            get { return OnProgressProc; } set { OnProgressProc = value; }
        }
        public CreatePersonsCompleteDelegate CompleteProc
        {
            get { return OnCompleteProc; } set { OnCompleteProc = value; }
        }
        public CreatePersonsErrorDelegate ErrorProc
        {
            get { return OnErrorProc; } set { OnErrorProc = value; }
        }

        public void Create()
        {
            if (isDisposed) throw new ObjectDisposedException("Persons");
            CPLDeleagte CreatePersons = this.CreatePersonList;
            CreatePersons.BeginInvoke(this.CreatePersonListComplete, CreatePersons);
        }
        public void Abort(bool value)
        {
            if (isDisposed) throw new ObjectDisposedException("Persons");
            this.abort = value;
        }
       
        private bool CreatePersonList()
        {
            // Для примера - здесь ничего полезного не происходит
            // однако в реальной программе вызываются функции из внешнего неуправляемого
            // ресурса (С++ dll)
            if (this.persons != null) this.persons.Clear();
            try
            {
                int i = 0;
                Person person = null;
                while (!abort && (i < this.persons.Capacity))
                {
                    // в этом блоке происходит два вызоыва из внешней длл
                    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

                    person = new Person("Gans-" + i, "Mustermann", "Herr", "Friedhof-" + i);
                    this.persons.Add(person);

                    /* Eсли генирировать события то просходит странная ошибка
                     * в клиенте время от времени вылетает ToolStripStatusLabel,
                     * где мы в данном случае выводим инфу о ходе операции
                     * причина значение индекса не должно быть отрицательным, и больше нуля
                     * но если увеличить шаг до 128 то ошибка не появляется покрайней мере у меня,
                     * с делегатами напрямую такой ошибки никогда нет и не важно как часто вызывать
                     * процедуру вывода, опять-таки может только у меня... */
                    if ((i++ % 8) == 0)
                        this.DoWait(this.CreateProgress, person, this.pStepEvent);                 

                    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                }
                this.DoWait(this.CreateProgress, person, this.pStepEvent);
                return true;
            }
            catch (Exception ex)
            {
                this.DoWait(this.CreateError, ex, this.pStepEvent);
                return false;
            }
        }
        private void CreatePersonListComplete(IAsyncResult ar)
        {
            CPLDeleagte cp = (CPLDeleagte)ar.AsyncState;
            if (cp.EndInvoke(ar)) this.OnCompleteProc(this.persons);
        }
        private void CreateError(object state)
        {
            this.OnErrorProc((Exception)state);
            this.pStepEvent.Set();
        }
        private void CreateProgress(object state)
        {
            this.OnProgressProc((Person)state);
            this.pStepEvent.Set();
        }
        private void DoWait(WaitCallback waitCallBack, Person person, AutoResetEvent ev)
        {
            ThreadPool.QueueUserWorkItem(waitCallBack,person);
            ev.WaitOne();
        }
        private void DoWait(WaitCallback waitCallBack, Exception ex, AutoResetEvent ev)
        {
            ThreadPool.QueueUserWorkItem(waitCallBack,ex);
            ev.WaitOne();
        }
       
        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }
        protected void Dispose(bool disposing)
        {
            if (!isDisposed)
            {
                if (disposing)
                {
                    this.abort = true;
                    this.persons.Clear();
                    this.persons = null;
                    this.OnCompleteProc = null;
                    this.OnErrorProc = null;
                    this.OnProgressProc = null;
                    this.pStepEvent.Close();
                    this.pStepEvent = null;
                }
                // clear some unmanaged resources
            }
            this.isDisposed = true;
        }
    }
}

Класс-3
Код: Выделить всё
using System;
using System.Threading;
using System.Drawing;
using System.Windows.Forms;
using System.Collections.Generic;

namespace WindowsApplication2
{
    public class PersonsDialog : Form
    {
        private System.ComponentModel.IContainer components = null;

        private void InitializeComponent()
        {
            this.menuStrip1 = new System.Windows.Forms.MenuStrip();
            this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.startToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator();
            this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.statusStrip1 = new System.Windows.Forms.StatusStrip();
            this.infoLabel = new System.Windows.Forms.ToolStripStatusLabel();
            this.listView1 = new System.Windows.Forms.ListView();
            this.columnHeader1 = new System.Windows.Forms.ColumnHeader();
            this.columnHeader2 = new System.Windows.Forms.ColumnHeader();
            this.columnHeader3 = new System.Windows.Forms.ColumnHeader();
            this.columnHeader4 = new System.Windows.Forms.ColumnHeader();
            this.menuStrip1.SuspendLayout();
            this.statusStrip1.SuspendLayout();
            this.SuspendLayout();
            //
            // menuStrip1
            //
            this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.fileToolStripMenuItem});
            this.menuStrip1.Location = new System.Drawing.Point(0, 0);
            this.menuStrip1.Name = "menuStrip1";
            this.menuStrip1.Size = new System.Drawing.Size(660, 24);
            this.menuStrip1.TabIndex = 0;
            this.menuStrip1.Text = "menuStrip1";
            //
            // fileToolStripMenuItem
            //
            this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.startToolStripMenuItem,
            this.toolStripMenuItem1,
            this.exitToolStripMenuItem});
            this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
            this.fileToolStripMenuItem.Size = new System.Drawing.Size(35, 20);
            this.fileToolStripMenuItem.Text = "File";
            //
            // startToolStripMenuItem
            //
            this.startToolStripMenuItem.Name = "startToolStripMenuItem";
            this.startToolStripMenuItem.Size = new System.Drawing.Size(109, 22);
            this.startToolStripMenuItem.Text = "Start";
            this.startToolStripMenuItem.Click += new System.EventHandler(this.startToolStripMenuItem_Click);
            //
            // toolStripMenuItem1
            //
            this.toolStripMenuItem1.Name = "toolStripMenuItem1";
            this.toolStripMenuItem1.Size = new System.Drawing.Size(106, 6);
            //
            // exitToolStripMenuItem
            //
            this.exitToolStripMenuItem.Name = "exitToolStripMenuItem";
            this.exitToolStripMenuItem.Size = new System.Drawing.Size(109, 22);
            this.exitToolStripMenuItem.Text = "Exit";
            this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click);
            //
            // statusStrip1
            //
            this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.infoLabel});
            this.statusStrip1.Location = new System.Drawing.Point(0, 377);
            this.statusStrip1.Name = "statusStrip1";
            this.statusStrip1.Size = new System.Drawing.Size(660, 22);
            this.statusStrip1.TabIndex = 1;
            this.statusStrip1.Text = "statusStrip1";
            //
            // infoLabel
            //
            this.infoLabel.Name = "infoLabel";
            this.infoLabel.Size = new System.Drawing.Size(645, 17);
            this.infoLabel.Spring = true;
            this.infoLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
            //
            // listView1
            //
            this.listView1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top
               | System.Windows.Forms.AnchorStyles.Bottom)
                    | System.Windows.Forms.AnchorStyles.Left)
                    | System.Windows.Forms.AnchorStyles.Right)));
            this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
            this.columnHeader1,
            this.columnHeader2,
            this.columnHeader3,
            this.columnHeader4});
            this.listView1.Location = new System.Drawing.Point(12, 36);
            this.listView1.Name = "listView1";
            this.listView1.Size = new System.Drawing.Size(636, 332);
            this.listView1.TabIndex = 2;
            this.listView1.UseCompatibleStateImageBehavior = false;
            this.listView1.View = System.Windows.Forms.View.Details;
            //
            // columnHeader1
            //
            this.columnHeader1.Text = "Title";
            this.columnHeader1.Width = 80;
            //
            // columnHeader2
            //
            this.columnHeader2.Text = "Name";
            this.columnHeader2.Width = 136;
            //
            // columnHeader3
            //
            this.columnHeader3.Text = "LastName";
            this.columnHeader3.Width = 135;
            //
            // columnHeader4
            //
            this.columnHeader4.Text = "Adress";
            this.columnHeader4.Width = 261;
            //
            // Form1
            //
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(660, 399);
            this.Controls.Add(this.listView1);
            this.Controls.Add(this.statusStrip1);
            this.Controls.Add(this.menuStrip1);
            this.MainMenuStrip = this.menuStrip1;
            this.Name = "Form1";
            this.Text = "Form1";
            this.menuStrip1.ResumeLayout(false);
            this.menuStrip1.PerformLayout();
            this.statusStrip1.ResumeLayout(false);
            this.statusStrip1.PerformLayout();
            this.ResumeLayout(false);
            this.PerformLayout();
        }

        private System.Windows.Forms.MenuStrip menuStrip1;
        private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem;
        private System.Windows.Forms.ToolStripMenuItem startToolStripMenuItem;
        private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1;
        private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem;
        private System.Windows.Forms.StatusStrip statusStrip1;
        private System.Windows.Forms.ListView listView1;
        private System.Windows.Forms.ColumnHeader columnHeader1;
        private System.Windows.Forms.ColumnHeader columnHeader2;
        private System.Windows.Forms.ColumnHeader columnHeader3;
        private System.Windows.Forms.ToolStripStatusLabel infoLabel;
        private System.Windows.Forms.ColumnHeader columnHeader4;
        private Persons persons;

        public PersonsDialog()
        {
            this.InitializeComponent();
            Control.CheckForIllegalCrossThreadCalls = false;
        }

        protected override void Dispose(bool disposing)
        {
            if (this.persons != null) this.persons.Dispose();
 
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Dispose();
        }
       
        private void startToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.persons != null) this.persons.Dispose();

            this.persons = new Persons(10000);
            this.persons.ProgressProc = delegate(Person p) {  this.infoLabel.Text = p.ToString(); };
            this.persons.CompleteProc = this.CreatePersonListComplete;
            this.persons.ErrorProc = this.CreatePersonListError;
            this.persons.Create();
        }

        private void CreatePersonListError(Exception exception)
        {
            try
            {
                throw exception;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void CreatePersonListComplete(List<Person> personsList)
        {
            try
            {
                // Подлый листвью не корректно обновляется есть идеи как обойти это ?
                //this.listView1.SuspendLayout();
                this.listView1.BeginUpdate();
                this.listView1.Items.Clear();
                foreach (Person p in personsList)
                {
                    ListViewItem lvi = this.listView1.Items.Add(p.Title);
                    lvi.SubItems.Add(p.Name);
                    lvi.SubItems.Add(p.LastName);
                    lvi.SubItems.Add(p.Adress);
                }
                this.listView1.EndUpdate();
                //this.listView1.ResumeLayout();
            }
            catch (Exception ex)
            {
                //
            }
            finally
            {
                //
            }
        }
    }

    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new PersonsDialog());
        }
    }
}


заранее благодарен за помощь.
У вас нет доступа для просмотра вложений в этом сообщении.


Вернуться в C#

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 20

    TopList