Как я могу синхронизировать эти два потока? - PullRequest
1 голос
/ 20 декабря 2010

вот класс:

public class Ticker
{
    public event EventHandler Tick;
    public EventArgs e = null;
    public void TickIt()
    {
        while (true)
        {
            System.Threading.Thread.Sleep(300);
            if (Tick != null)
            {
                Tick(this, e);
            }
        }
    }

Я запускаю два потока в форме окна:

public partial class Form1 : Form
{
    Ticker ticker1 = new Ticker();
    Ticker ticker2 = new Ticker();
    Thread t;
    Thread t1;

    public Form1()
    {
        InitializeComponent();
        ticker1.Tick += ticker1_Tick;
        ticker2.Tick += ticker2_Tick;

        t = new Thread(new ThreadStart(ticker1.TickIt));
        t1 = new Thread(new ThreadStart(ticker2.TickIt)));
        t.Start();
        t1.Start();

    }
    public void ticker1_Tick(object sender, EventArgs e)
    {
        if (this.InvokeRequired)
        {
            this.BeginInvoke((MethodInvoker)delegate
            {
                ticker1_Tick(sender, e);
            });
            return;
        } 


        richTextBox1.Text += "t1 ";
    }
    public void ticker2_Tick(object sender, EventArgs e)
    {
        if (this.InvokeRequired)
        {
            this.BeginInvoke((MethodInvoker)delegate
            {
                ticker2_Tick(sender, e);
            });
            return;
        } 


        richTextBox2.Text += "t2 ";
    }

Проблема в том, что через несколько секунд поток t опережает t1несколько тиков.

Прежде всего, почему это происходит, это не имеет смысла, поскольку каждый поток должен ждать 300 мс, прежде чем тикать?

Во-вторых, как я могу синхронизировать эти два потока,поэтому они тикают одновременно, а один не опережает другого?

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

Ответы [ 5 ]

2 голосов
/ 20 декабря 2010

Как насчет использования AutoResetEvent ?

class Program
{
    static readonly AutoResetEvent thread1Step = new AutoResetEvent(false);
    static readonly AutoResetEvent thread2Step = new AutoResetEvent(false);

    static void Main(string[] args)
    {
        new Thread(new ThreadStart(Thread1Main)).Start();
        new Thread(new ThreadStart(Thread2Main)).Start();
    }

    private static void Thread1Main()
    {
        for (int i = 0; i < int.MaxValue; i++)
        {
            Console.WriteLine("thread1 i=" + i);
            thread1Step.Set();
            thread2Step.WaitOne();
        }
    }

    private static void Thread2Main()
    {
        for (int i = 0; i < int.MaxValue; i++)
        {
            Console.WriteLine("thread2 i=" + i);
            thread2Step.Set();
            thread1Step.WaitOne();
        }
    }
}
2 голосов
/ 20 декабря 2010

Если вам действительно нужно, чтобы они были идеально синхронизированы и выполняли тики в определенном порядке, вам понадобится какой-то центральный таймер, как упоминал Хайме.Если вам нужна независимая синхронизация, но вы хотите предотвратить смещение, вызванное неточностью Sleep, или задержку, добавленную к времени, которое требуется для выполнения обработчика событий, то будет работать примерно так:

public class Ticker
{
    public event EventHandler Tick;
    public EventArgs e = null;
    public void TickIt()
    {
        const int targetSleepTime = 300;
        int nextTick = Environment.TickCount + targetSleepTime;
        while (true)
        {
            System.Threading.Thread.Sleep(Math.Max(nextTick - Environment.TickCount, 0));
            if (Tick != null)
            {
                Tick(this, e);
            }
            nextTick += targetSleepTime;
        }
    }
}

Просто имейте в виду, что Environment.TickCount может возвращаться к Int32.MinValue, когда он попадает в Int32.MaxValue.Вам понадобится дополнительный код, чтобы справиться с этим, или, возможно, основывать синхронизацию на DateTime.UtcNow (меньше накладных расходов, чем DateTime.Now).

2 голосов
/ 20 декабря 2010

Я не думаю, что вы можете доверять sleep (300), чтобы ваши потоки работали одинаковое количество раз независимо ...

Одна вещь, которую вы могли бы сделать, это иметь центральный генератор таймеров / тиков, который сигнализирует объект синхронизации на каждом тике, а функция потока тикает только один раз, а затем WaitsForObject для следующего тика, генерируемого из основного потока, эффективно один таймер и говорит потокам синхронно.

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

0 голосов
/ 21 декабря 2010

В своем классе Ticker увеличьте частоту опроса и проверяйте системный таймер, пока не достигнете нужного интервала.Вы можете использовать TickCount или Ticks , если вы можете жить с точностью до миллисекунды, или использовать StopWatch для более высокой точности, если ваша система поддерживает это.

Чтобы синхронизировать их, для начала им потребуется общая ссылка.Вы можете передать это как определенный будущий тик, чтобы начать синхронизацию, или использовать что-то вроде Tick модуль 100. Или опросить общий статический флаг, который указывает, когда начинать.

Вы не можете иметь абсолютную точность, поэтомуопределить, с каким диапазоном точности вы можете жить с самого начала, например, плюс-или-минус 5 мс между вашими потоками Ticker.

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

0 голосов
/ 20 декабря 2010

Хорошо, вы можете использовать Barrier, если вы используете .NET 4.0, но вам придется поместить его в ваш класс Ticker, иначе вы заблокируете ваш поток пользовательского интерфейса.

...