← Zurück

В lock путаются очередь

Только сейчас заметил, что в lock путается очередь. Вот простой код для проверки


        private Thread t;
        private int i = 0;
        private readonly SyncObject _syncLock = new SyncObject();
        private void button1_Click(object sender, RoutedEventArgs e)
        {
            i++;
            t = new Thread(SSS);
            t.Start(i);
        }

        private void SSS(object ii)
        {
            lock (_syncLock)
            {
                Debug.WriteLine("Отправка " + ii);
                Thread.Sleep(3000);
                Debug.WriteLine("Получение " + ii);
            }
        }

Результат

Отправка 1
Получение 1
Поток '<Без имени>' (0x1c98) завершился с кодом 0 (0x0).
Отправка 10
Получение 10
Поток '<Без имени>' (0x1b28) завершился с кодом 0 (0x0).
Отправка 9
Получение 9
Поток '<Без имени>' (0x2350) завершился с кодом 0 (0x0).
Отправка 7
Получение 7
Поток '<Без имени>' (0x1d68) завершился с кодом 0 (0x0).
Отправка 8
Получение 8
Поток '<Без имени>' (0x2338) завершился с кодом 0 (0x0).
Отправка 6
Получение 6
Поток '<Без имени>' (0x20c4) завершился с кодом 0 (0x0).
Отправка 5
Получение 5
Поток '<Без имени>' (0x240) завершился с кодом 0 (0x0).
Отправка 4
Получение 4
Поток '<Без имени>' (0x1e8c) завершился с кодом 0 (0x0).
Отправка 2
Получение 2
Поток '<Без имени>' (0x470) завершился с кодом 0 (0x0).
Отправка 3
Получение 3
Поток '<Без имени>' (0x1f20) завершился с кодом 0 (0x0).

Мне кажется такого же не должно быть. Или я где то не прав?

War dieses Thema hilfreich?

Thema teilen

Kommentare (11)

Anmelden oder Konto erstellen, Melden Sie sich an oder registrieren Sie sich, um einen Kommentar zu hinterlassen

VassilSanych
VassilSanych

Создание потока без пула - штука тяжёлая. Скорее всего это не lock путается, а то что приходит в него. PS Попробуйте Task.Run(...)

casper-ss
casper-ss

А можно по подробнее про Task.Run...если не сложно...что это и с чем едят?первый раз увидел просто...

yar1k0v
yar1k0v

Task - задача для выполнения в фоновом потоке


new System.Threading.Tasks.Task(() => { }).Start();
или
new System.Threading.Tasks.TaskFactory().StartNew(() => { }).Start();

yar1k0v
yar1k0v

private Thread t; private int i = 0; private readonly SyncObject _syncLock = new SyncObject(); private void button1_Click(object sender, RoutedEventArgs e) { i++; t = new Thread(SSS); t.Start(i); }

private void SSS(object ii) { lock (_syncLock) { Debug.WriteLine("Отправка " + ii); Thread.Sleep(3000); Debug.WriteLine("Получение " + ii); } }

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

MenDel
MenDel Autor

...

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

Суть кода в том что в основном потоке i итерируется, а в другом обрабатывается. В основном потоке лок на i нельзя накладывать.

MenDel
MenDel Autor

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

BlockingCollection<string[]> _queue = new BlockingCollection<string[]>(new ConcurrentQueue<string[]>());
Thread bt = new Thread(() =>
                {
                    while (true)
                    {
                        // Thread.Sleep(1000); Можно добавить
                        if (_queue.Count == 0) continue;
                        var val = _queue.Take();
                        SmsSend(val); // Метод которому посылается val
                    }
                }) { Name = "bt", IsBackground = true };
backThread.Start(); 
yar1k0v
yar1k0v

Суть кода в том что в основном потоке i итерируется, а в другом обрабатывается. В основном потоке лок на i нельзя накладывать.

Вот пример:


        int i = 0;
        static object locker = new object();

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Start_Click(object sender, RoutedEventArgs e)
        {
            lock (locker) { i++; }
            new System.Threading.Thread(() =>
            {
                lock (locker)
                {
                    this.Dispatcher.BeginInvoke((Action)delegate()
                    {
                        Text.Text = "Started " + i.ToString();
                    });
                    System.Threading.Thread.Sleep(2000);
                    this.Dispatcher.BeginInvoke((Action)delegate() 
                    {
                        Text.Text += Environment.NewLine;
                        Text.Text += "Finished " + i.ToString();
                    });
                }  
            }).Start();
        }

В данном примере ничего не путается.

Почему lock на переменную итерации нельзя накладывать? Расскажите пожалуйста, буду знать. Спасибо!

MenDel
MenDel Autor

Суть кода в том что в основном потоке i итерируется, а в другом обрабатывается. В основном потоке лок на i нельзя накладывать.

Вот пример:


        int i = 0;
        static object locker = new object();

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Start_Click(object sender, RoutedEventArgs e)
        {
            lock (locker) { i++; }
            new System.Threading.Thread(() =>
            {
                lock (locker)
                {
                    this.Dispatcher.BeginInvoke((Action)delegate()
                    {
                        Text.Text = "Started " + i.ToString();
                    });
                    System.Threading.Thread.Sleep(2000);
                    this.Dispatcher.BeginInvoke((Action)delegate() 
                    {
                        Text.Text += Environment.NewLine;
                        Text.Text += "Finished " + i.ToString();
                    });
                }  
            }).Start();
        }

В данном примере ничего не путается.

Почему lock на переменную итерации нельзя накладывать? Расскажите пожалуйста, буду знать. Спасибо!

Если усыпить поток не на 2, а на 5 секунд вот, что получилось (я нажал на кнопку 15 раз) Результаты вывел не на форму, а в дебаг

Started 1
Поток '<Без имени>' (0x2bb4) завершился с кодом 0 (0x0).
Finished 2
Started 2
Поток '<Без имени>' (0x2a1c) завершился с кодом 0 (0x0).
Finished 3
Started 3
Поток '<Без имени>' (0x1830) завершился с кодом 0 (0x0).
Поток '<Без имени>' (0x1fe4) завершился с кодом 0 (0x0).
Finished 4
Started 4
Поток '<Без имени>' (0x14ac) завершился с кодом 0 (0x0).
Finished 5
Started 5
Поток '<Без имени>' (0x2934) завершился с кодом 0 (0x0).
Finished 5
  1. В результате сработало только 5 раз из 15
  2. Форма повисла
  3. Не понял по какой причине в Finished число идет на 1 больше

Есть еще мысли?

yar1k0v
yar1k0v

Прости, у меня такой проблемы не возникало. И сам пример, даже после нажатия 15 раз нормально работает. Может скажешь какова суть эксперимента? Возможно я смогу помочь по более конкретной задаче? Зависать может из за нехватки ресурсов, хз... Я не знаю как устроен метод Sleep(), я лишь знаю что он делает. Но видимо в нем причина зависания. Возможно пока он слипит текущий поток, запускается следующий. Ну и из за этого неполадки.

П.С.

Для меня например до сих пор загадка принцип работы new Random().Next(int min, int max) =) а именно почему при одновременном вызове нескольких таких методов, возвращаемые значения идентичны?

MenDel
MenDel Autor

Про Random мне тоже интересно почему так.

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

yar1k0v
yar1k0v

Попробуй AutoResetEvent... Пример использования есть в примере S# QUIK Console, что то такое... Там как раз то что нужно)