C# Monitor работает вообще ?
От: Rumata_V Украина  
Дата: 12.10.06 11:45
Оценка:
Собственно долго ломал голову и так и не нашел ответа на вопрос почему...

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

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace test
{
    public partial class Form1 : Form
    {
        private List<Thread> _threads;
        private bool _working = true;

        public Form1()
        {
            InitializeComponent();
            _threads = new List<Thread>();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            _working = true;
            Thread t;
            for (int i = 0; i < 2; i++)
            {
                t = new Thread(new ThreadStart(ExecuteThreadProc));
                _threads.Add(t);
                t.Start();
                label1.Text = _threads.Count.ToString();
                Application.DoEvents();
            }
        }
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            _working = false;
        }
        private void ExecuteThreadProc()
        {
            int count = 0;
            int threadID = Thread.CurrentThread.ManagedThreadId;
            string str;
            StringBuilder sb = new StringBuilder();
            
            while(_working)
            {
                sb.Length = 0;
                str = string.Format("ThreadID = {0}, Count={1}", threadID, count++);
                for (int i = 0; i < 100; i++)
                    sb.AppendFormat("{0} ", threadID);
                _OnMessage(str, sb.ToString());
                Thread.Sleep(200);
            }
        }
        private delegate void d_OnMessage(string str, string s);
        private void _OnMessage(string str, string s)
        {
            if (InvokeRequired)
            {
                try
                {
                    Monitor.Enter(listBox1);
                    int id = Thread.CurrentThread.ManagedThreadId; // это просто для контроля, чтобы быть уверенным что потоки действительно разные...
                    BeginInvoke(new d_OnMessage(_OnMessage), new object[] { str, s });
                }
                finally
                {
                    Monitor.Exit(listBox1);    
                }
            }
            else
            {
                listBox1.BeginUpdate();
                listBox1.Items.Insert(0, listBox1.Items.Count.ToString("D3") + " " + str + " --> " + s);
                label1.Text = listBox1.Items.Count.ToString();
                while (listBox1.Items.Count >= 10)
                    listBox1.Items.Remove(listBox1.Items.Count - 1); //!!! вот тут приложение ложится насмерть...
                listBox1.EndUpdate();
            }
        }
        private void button2_Click(object sender, EventArgs e)
        {
            _working = false;
            _threads.Clear();
            label1.Text = _threads.Count.ToString();
        }
    }
}


если я правильно понял... что Monitor.Enter Acquires an exclusive lock on the specified object. из МСДН.
значит код не должен одновременно выполняться одновременно... если убрать удаление, то работает
как только выполняется listBox1.Items.Remove(listBox1.Items.Count — 1); приложение ложится....

так работает ли Monitor? и в каких случаях, потому что в случае написанного в МСДН он не работает... или я не так понял?
Re: C# Monitor работает вообще ?
От: Morpheus_  
Дата: 12.10.06 12:06
Оценка: -1
Здравствуйте, Rumata_V, Вы писали:

R_V>Собственно долго ломал голову и так и не нашел ответа на вопрос почему...


R_V>задача простая... лог со скроллом вниз, Если количество строчек больше заданного числа, последняя строчка удаляется.


R_V>
R_V>    public partial class Form1 : Form
R_V>


во втором фреймворке есть семафоры, см. Semaphore, ипользуй их...
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re: C# Monitor работает вообще ?
От: chabster Украина chabster.blogspot.com
Дата: 12.10.06 12:53
Оценка: +1
А какую из двух строчек кода защищает монитор и зачем?

try
{
Monitor.Enter(listBox1);
int id = Thread.CurrentThread.ManagedThreadId; // это просто для контроля, чтобы быть уверенным что потоки действительно разные...
BeginInvoke(new d_OnMessage(_OnMessage), new object[] { str, s });
}
finally
{
Monitor.Exit(listBox1);
}
Posted via RSDN NNTP Server 2.0
Re: C# Monitor работает вообще ?
От: Lloyd Россия  
Дата: 12.10.06 12:54
Оценка:
Здравствуйте, Rumata_V, Вы писали:

R_V>если я правильно понял... что Monitor.Enter Acquires an exclusive lock on the specified object. из МСДН.

R_V>значит код не должен одновременно выполняться одновременно... если убрать удаление, то работает
R_V>как только выполняется listBox1.Items.Remove(listBox1.Items.Count — 1); приложение ложится....

R_V>так работает ли Monitor? и в каких случаях, потому что в случае написанного в МСДН он не работает... или я не так понял?


Используй не BeginInvoke, а просто Invoke. BeginInvoke работает асинхронно.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re: C# Monitor работает вообще ?
От: Красин Россия  
Дата: 12.10.06 13:09
Оценка: 5 (2)
Здравствуйте, Rumata_V, Вы писали:

R_V>Собственно долго ломал голову и так и не нашел ответа на вопрос почему...


R_V>задача простая... лог со скроллом вниз, Если количество строчек больше заданного числа, последняя строчка удаляется.

public partial class Form1 : Form
{
    private List<Thread> _threads = new List<Thread>();
    private volatile bool _working = true; //К этой переменной идет обращение из нескольких поток,
//она должна быть volatile

    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        _working = true;
        Thread t;
        for (int i = 0; i < 2; i++)
        {
            t = new Thread(new ThreadStart(ExecuteThreadProc));
            _threads.Add(t);
            t.Start();
            label1.Text = _threads.Count.ToString();
            Application.DoEvents();
        }
    }
    private void ExecuteThreadProc()
    {
        int count = 0;
        int threadID = Thread.CurrentThread.ManagedThreadId;
        string str;
        StringBuilder sb = new StringBuilder();

        while (_working)
        {
            sb.Length = 0;
            str = string.Format("ThreadID = {0}, Count={1}", threadID, count++);
            for (int i = 0; i < 100; i++)
                sb.AppendFormat("{0} ", threadID);
            _OnMessage(str, sb.ToString());
            Thread.Sleep(200);
        }
    }

    private delegate void d_OnMessage(string str, string s);
    private void _OnMessage(string str, string s)
    {
        if (InvokeRequired)
        {
//Здесь убран монитор. Т.к. нафиг не нужен - в монитор входит поток, а BeginInvoke всего лишь говорит главному потоку: исполни делегат
//и не дожидаясь выполнения метода выходит. 
            BeginInvoke(new d_OnMessage(_OnMessage), new object[] { str, s });
        }
        else
        {
//Тут синхронизация тоже нафиг не нужна - все и так исполняется в одном, главном потоке формы.
            listBox1.BeginUpdate();
            listBox1.SuspendLayout();//Чтобы не мигало
            listBox1.Items.Insert(0, listBox1.Items.Count.ToString("D3") + " " + str + " --> " + s);
            label1.Text = listBox1.Items.Count.ToString();
            while (listBox1.Items.Count >= 10)
                listBox1.Items.RemoveAt(listBox1.Items.Count - 1); //Здесь был .Remove. А удалить надо не строку с числом,
//а последнюю строку. Т.е. это RemoveAt
            listBox1.ResumeLayout();
            listBox1.EndUpdate();
        }
    }
    private void button2_Click(object sender, EventArgs e)
    {
        _working = false;
        _threads.Clear();
        label1.Text = _threads.Count.ToString();
    }

    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        _working = false;
    }
}


К комментариям в коде добавлю, что для того, чтобы войти в монитор в C# есть специальная конструкция lock (см msdn).
Re[2]: C# Monitor работает вообще ?
От: Красин Россия  
Дата: 12.10.06 13:10
Оценка:
Здравствуйте, Morpheus_, Вы писали:


M_>во втором фреймворке есть семафоры, см. Semaphore, ипользуй их...


В данном случае не нужно использовать ни мониторы, ни семафоры.
Re[2]: C# Monitor работает вообще ?
От: Rumata_V Украина  
Дата: 12.10.06 13:12
Оценка:
Здравствуйте, chabster, Вы писали:

C>А какую из двух строчек кода защищает монитор и зачем?


C> try

C> {
C> Monitor.Enter(listBox1);
C> int id = Thread.CurrentThread.ManagedThreadId; // это просто для контроля, чтобы быть уверенным что потоки действительно разные...
C> BeginInvoke(new d_OnMessage(_OnMessage), new object[] { str, s });
C> }
C> finally
C> {
C> Monitor.Exit(listBox1);
C> }

да собственно эту

BeginInvoke(new d_OnMessage(_OnMessage), new object[] { str, s });

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

Да и решение было не в этом а...

listBox1.Items.Remove(listBox1.Items.Count — 1);
надо заменить на
listBox1.Items.RemoveAt(listBox1.Items.Count — 1);
или
listBox1.Items.Remove(listBox1.Items[listBox1.Items.Count — 1]);

собственно я удивлен, что не выскакивала ошибка, а приложение валилось, потому грешил на синхронизацию...
Невнимательность ...
Re[3]: C# Monitor работает вообще ?
От: Красин Россия  
Дата: 12.10.06 13:14
Оценка:
Здравствуйте, Rumata_V, Вы писали:

R_V>Да и решение было не в этом а...


R_V>listBox1.Items.Remove(listBox1.Items.Count — 1);

R_V>надо заменить на
R_V>listBox1.Items.RemoveAt(listBox1.Items.Count — 1);
R_V>или
R_V>listBox1.Items.Remove(listBox1.Items[listBox1.Items.Count — 1]);

Второй способ не подойдет в случае, если в ListBox есть еще одна строка равная последней.
Re[2]: C# Monitor работает вообще ?
От: Rumata_V Украина  
Дата: 12.10.06 13:15
Оценка:
Здравствуйте, Красин, Вы писали:

К>Здравствуйте, Rumata_V, Вы писали:


R_V>>Собственно долго ломал голову и так и не нашел ответа на вопрос почему...


R_V>>задача простая... лог со скроллом вниз, Если количество строчек больше заданного числа, последняя строчка удаляется.

К>
К>public partial class Form1 : Form
К>{
К>    private List<Thread> _threads = new List<Thread>();
К>    private volatile bool _working = true; //К этой переменной идет обращение из нескольких поток,
К>//она должна быть volatile

К>    public Form1()
К>    {
К>        InitializeComponent();
К>    }

К>    private void button1_Click(object sender, EventArgs e)
К>    {
К>        _working = true;
К>        Thread t;
К>        for (int i = 0; i < 2; i++)
К>        {
К>            t = new Thread(new ThreadStart(ExecuteThreadProc));
К>            _threads.Add(t);
К>            t.Start();
К>            label1.Text = _threads.Count.ToString();
К>            Application.DoEvents();
К>        }
К>    }
К>    private void ExecuteThreadProc()
К>    {
К>        int count = 0;
К>        int threadID = Thread.CurrentThread.ManagedThreadId;
К>        string str;
К>        StringBuilder sb = new StringBuilder();

К>        while (_working)
К>        {
К>            sb.Length = 0;
К>            str = string.Format("ThreadID = {0}, Count={1}", threadID, count++);
К>            for (int i = 0; i < 100; i++)
К>                sb.AppendFormat("{0} ", threadID);
К>            _OnMessage(str, sb.ToString());
К>            Thread.Sleep(200);
К>        }
К>    }

К>    private delegate void d_OnMessage(string str, string s);
К>    private void _OnMessage(string str, string s)
К>    {
К>        if (InvokeRequired)
К>        {
К>//Здесь убран монитор. Т.к. нафиг не нужен - в монитор входит поток, а BeginInvoke всего лишь говорит главному потоку: исполни делегат
К>//и не дожидаясь выполнения метода выходит. 
К>            BeginInvoke(new d_OnMessage(_OnMessage), new object[] { str, s });
К>        }
К>        else
К>        {
К>//Тут синхронизация тоже нафиг не нужна - все и так исполняется в одном, главном потоке формы.
К>            listBox1.BeginUpdate();
К>            listBox1.SuspendLayout();//Чтобы не мигало
К>            listBox1.Items.Insert(0, listBox1.Items.Count.ToString("D3") + " " + str + " --> " + s);
К>            label1.Text = listBox1.Items.Count.ToString();
К>            while (listBox1.Items.Count >= 10)
К>                listBox1.Items.RemoveAt(listBox1.Items.Count - 1); //Здесь был .Remove. А удалить надо не строку с числом,
К>//а последнюю строку. Т.е. это RemoveAt
К>            listBox1.ResumeLayout();
К>            listBox1.EndUpdate();
К>        }
К>    }
К>    private void button2_Click(object sender, EventArgs e)
К>    {
К>        _working = false;
К>        _threads.Clear();
К>        label1.Text = _threads.Count.ToString();
К>    }

К>    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
К>    {
К>        _working = false;
К>    }
К>}
К>


К>К комментариям в коде добавлю, что для того, чтобы войти в монитор в C# есть специальная конструкция lock (см msdn).



спасибо, очень дельные советы... Избыточность кода... потому что не знал что делать, почему? Грешил на синхронизацию, потому писал локи и мониторы где попало...
ошибка была именно в listBox1.Items.RemoveAt(listBox1.Items.Count — 1);
ну можно было бы ещё listBox1.Items.RemoveAt(listBox1.Items[listBox1.Items.Count — 1]);
бес попутал... стер все, переписал код заново, заработало, потом проанализировал отличия... и вот...
Re[4]: C# Monitor работает вообще ?
От: Rumata_V Украина  
Дата: 12.10.06 13:18
Оценка:
Здравствуйте, Красин, Вы писали:

К>Здравствуйте, Rumata_V, Вы писали:


R_V>>Да и решение было не в этом а...


R_V>>listBox1.Items.Remove(listBox1.Items.Count — 1);

R_V>>надо заменить на
R_V>>listBox1.Items.RemoveAt(listBox1.Items.Count — 1);
R_V>>или
R_V>>listBox1.Items.Remove(listBox1.Items[listBox1.Items.Count — 1]);

К>Второй способ не подойдет в случае, если в ListBox есть еще одна строка равная последней.


ну это уже... лол...
хотя согласен, такое возможно, при этом удалится N строчек... с указателем на оригинал.
В данном случае я уверен, что указатели разные. они же генерятся потоками через стригбилдер.
Re[5]: C# Monitor работает вообще ?
От: Красин Россия  
Дата: 12.10.06 13:29
Оценка:
Здравствуйте, Rumata_V, Вы писали:

К>>Второй способ не подойдет в случае, если в ListBox есть еще одна строка равная последней.


R_V>ну это уже... лол...

R_V>хотя согласен, такое возможно, при этом удалится N строчек... с указателем на оригинал.
R_V>В данном случае я уверен, что указатели разные. они же генерятся потоками через стригбилдер.

Нет. Т.к. ListBox проверяет не на то, что ссылка та же, а вызывает метод Equals, который сравнивает строки. В доказательство код:

public partial class Form1 : Form
{    
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        listBox1.Items.Add("lala");
        listBox1.Items.Add("bb");
        listBox1.Items.Add(new string(new char[] { 'l', 'a', 'l', 'a' }));        
    }

    private void button2_Click(object sender, EventArgs e)
    {
        listBox1.Items.Remove(listBox1.Items[listBox1.Items.Count - 1]);
    }
}

Нажмите Button1, появится:
[quote]
lala
bb
lala
[/quote]
Нажмите Button2, появится:
[quote]
bb
lala
[/quote]
Re[3]: C# Monitor работает вообще ?
От: chabster Украина chabster.blogspot.com
Дата: 12.10.06 14:04
Оценка:
да собственно эту

BeginInvoke(new d_OnMessage(_OnMessage), new object[] { str, s });

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

Не будет и блок в else никогда параллельно не выполнится
Posted via RSDN NNTP Server 2.0
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.