Не могу понять: используя lock (), но никогда не выполняю код - PullRequest
2 голосов
/ 06 апреля 2011

Я изучаю потоки в C # и получаю такое поведение, которое не могу понять.

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

Четыре потока запущены.Каждый выполняет только счет.Это работает хорошо, я могу видеть на форме, растет число.Но есть кнопка для подсчета из потока формы.Когда я нажимаю на нее, основной поток останавливается.Отладчик показывает, что другие потоки продолжают считать, один за другим, но поток формы никогда не получает доступ к ресурсу.

1) Почему блокировка (tty) из потока формы никогда не получает доступ к нему, когдау других тем нет проблем?2) Есть ли лучший способ сделать этот тип синхронизации?

Извините за большой код:

public class MegaAPI
{
    public int SomeStupidBlockingFunction(int c)
    {
        Thread.Sleep(800);
        return ++c;
    }
}

class UIThread
{
    public delegate void dlComandoMaquina();

    public class T0_SyncEvents
    {
        private EventWaitHandle _EventFechar; // Exit thread event

        public T0_SyncEvents()
        {
            _EventFechar = new ManualResetEvent(false);
        }

        public EventWaitHandle EventFecharThread // Exit thread event
        {
            get { return _EventFechar; }
        }
    }

    public class T0_Thread
    {
        private T0_SyncEvents _syncEvents;
        private int _msTimeOut;

        private dlComandoMaquina _ComandoMaquina;

        public T0_Thread(T0_SyncEvents e, dlComandoMaquina ComandoMaquina, int msTimeOut)
        {
            _syncEvents = e;
            _msTimeOut = msTimeOut;
            _ComandoMaquina = ComandoMaquina;
        }

        public void VaiRodar() // thread running code
        {
            while (!_syncEvents.EventFecharThread.WaitOne(_msTimeOut, false))
            {
                _ComandoMaquina();
            }

        }
    }
}

public partial class Form1 : Form
{
    MegaAPI tty;

    UIThread.T0_Thread thr1;
    UIThread.T0_SyncEvents thrE1;
    Thread Thread1;
    int ACount1 = 0;

    void UIUpdate1()
    {
        lock (tty)
        {
            ACount1 = tty.SomeStupidBlockingFunction(ACount1);
        }
        this.BeginInvoke((Action)delegate { txtAuto1.Text = ACount1.ToString(); });
    }

    UIThread.T0_Thread thr2;
    UIThread.T0_SyncEvents thrE2;
    Thread Thread2;
    int ACount2 = 0;

    void UIUpdate2()
    {
        lock (tty)
        {
            ACount2 = tty.SomeStupidBlockingFunction(ACount2);
        }
        this.BeginInvoke((Action)delegate { txtAuto2.Text = ACount2.ToString(); });
    }

    UIThread.T0_Thread thr3;
    UIThread.T0_SyncEvents thrE3;
    Thread Thread3;
    int ACount3 = 0;

    void UIUpdate3()
    {
        lock (tty)
        {
            ACount3 = tty.SomeStupidBlockingFunction(ACount3);
        }
        this.BeginInvoke((Action)delegate { txtAuto3.Text = ACount3.ToString(); });
    }

    UIThread.T0_Thread thr4;
    UIThread.T0_SyncEvents thrE4;
    Thread Thread4;
    int ACount4 = 0;

    void UIUpdate4()
    {
        lock (tty)
        {
            ACount4 = tty.SomeStupidBlockingFunction(ACount4);
        }
        this.BeginInvoke((Action)delegate { txtAuto4.Text = ACount4.ToString(); });
    }


    public Form1()
    {
        InitializeComponent();

        tty = new MegaAPI();

        thrE1 = new UIThread.T0_SyncEvents();
        thr1 = new UIThread.T0_Thread(thrE1, UIUpdate1, 500);
        Thread1 = new Thread(thr1.VaiRodar);
        Thread1.Start();

        thrE2 = new UIThread.T0_SyncEvents();
        thr2 = new UIThread.T0_Thread(thrE2, UIUpdate2, 500);
        Thread2 = new Thread(thr2.VaiRodar);
        Thread2.Start();

        thrE3 = new UIThread.T0_SyncEvents();
        thr3 = new UIThread.T0_Thread(thrE3, UIUpdate3, 500);
        Thread3 = new Thread(thr3.VaiRodar);
        Thread3.Start();

        thrE4 = new UIThread.T0_SyncEvents();
        thr4 = new UIThread.T0_Thread(thrE4, UIUpdate4, 500);
        Thread4 = new Thread(thr4.VaiRodar);
        Thread4.Start();

    }

    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        thrE1.EventFecharThread.Set();
        thrE2.EventFecharThread.Set();
        thrE3.EventFecharThread.Set();
        thrE4.EventFecharThread.Set();

        Thread1.Join();
        Thread2.Join();
        Thread3.Join();
        Thread4.Join();
    }

    int Mcount = 0;
    private void btManual_Click(object sender, EventArgs e)
    {
        Cursor.Current = Cursors.WaitCursor;

        lock (tty)  // locks here ! Never runs inside! But the other threads keep counting..
        {
            Mcount = tty.SomeStupidBlockingFunction(Mcount);
            txtManual.Text = Mcount.ToString();
        }
        Cursor.Current = Cursors.Default;
    }
}

Ответы [ 2 ]

1 голос
/ 06 апреля 2011

Я подозреваю, что вы что-то делаете с циклом сообщений Windows и многопоточностью в WinForms. Я не знаю, что это такое, но вот несколько советов:

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

.
this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);

затем переключите ваш код в btManual_Click, чтобы вызвать фонового работника следующим образом:

backgroundWorker1.RunWorkerAsync();

и затем:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    Mcount = tty.SomeStupidBlockingFunction(Mcount);
    this.BeginInvoke((Action)delegate { txtManual.Text = Mcount.ToString(); });
}

Я пропустил блокировку (tty), потому что я предпочел бы видеть только одно из этих утверждений внутри функции, а не пять из них снаружи. И вместо блокировки на tty я бы создал приватную переменную, подобную этой:

public class MegaAPI
{
    private object sync = new object();

    public int SomeStupidBlockingFunction(int c)
    {
        lock (this.sync)
        {
            Thread.Sleep(800);
            return ++c;                
        }
    }
}

Затем все остальные упрощаются, например:

void UIUpdate1()
{
    ACount1 = tty.SomeStupidBlockingFunction(ACount1);
    this.BeginInvoke((Action)delegate { txtAuto1.Text = ACount1.ToString(); });
}

И поскольку вы не можете запустить фоновый рабочий, пока он еще обрабатывает, вот быстрое и грязное решение: отключите кнопку во время ее работы:

this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);

и затем:

private void btManual_Click(object sender, EventArgs e)
{
    this.btManual.Enabled = false;
    backgroundWorker1.RunWorkerAsync();
}

и

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    this.btManual.Enabled = true;
}

Поэтому я рекомендую:

  • Оставьте один оператор lock () внутри функции, нуждающейся в синхронизация
  • Хранить объект блокировки в секрете
  • Запустить работу на фоновом рабочем
0 голосов
/ 28 августа 2011

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

Если для вас проблема, если «неправильный поток» получает мьютекс, вы делаете это неправильно,Мьютексы предназначены для случаев, когда нет «неправильной нити».Если вам нужно честное или предсказуемое планирование, вам нужно использовать блокирующий примитив, который его предоставляет, или использовать приоритеты потоков.

Мьютексы обычно действуют странным образом, когда потоки, которые их содержат, не ограничены ЦП.Ваши нити приобретают мьютекс, а затем откладывают его.Это приведет к вырожденному поведению планирования точно так же, как поведение, которое вы видите.(Конечно, они не нарушат свои гарантии, но они будут действовать гораздо менее как теоретически совершенный мьютекс, который также обеспечивает такие вещи, как справедливость.)

...