Монитор. Ожидайте/Пульсируйте состояние состязания в многопоточном сервере

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

import matplotlib
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# make-up some data
goal_x = list(range(10))
goal_y = list(range(10))
goal_z = list(range(10))
epoch_arr = np.linspace(0,1,10)

fig = plt.figure(figsize=(8,8))
ax3D = fig.add_subplot(111, projection='3d')
ax3D.set_facecolor('xkcd:salmon')

# First plot: all markers are in white color
ax3D.scatter(goal_x, goal_y, goal_z, s=500, c='w', marker='o', alpha=1.0, zorder=10)

colormap = plt.get_cmap("binary")
norm = matplotlib.colors.Normalize(vmin=min(epoch_arr), vmax=max(epoch_arr))
#ax3D.scatter(goal_x, goal_y, goal_z, s=100, c=colormap(norm(epoch_arr.values)), marker='o')

# Second plot: use intended colormap
ax3D.scatter(goal_x, goal_y, goal_z, s=500, c='b', marker='o', zorder=11)

plt.show()

Результирующий сюжет:

enter image description here

8
задан 12 May 2009 в 08:00
поделиться

1 ответ

Проблема в том, что вы используете Pulse / Wait в качестве сигнала. Правильный сигнал, такой как AutoResetEvent, имеет такое состояние, что он остается активированным до тех пор, пока поток не вызовет WaitOne (). Вызов Pulse без каких-либо ожидающих его потоков превратится в noop.

Это сочетается с тем фактом, что блокировка может быть взята много раз одним и тем же потоком. Поскольку вы используете асинхронное программирование, обратный вызов Accept может быть вызван тем же потоком, что и BeginAcceptTcpClient.

Позвольте мне проиллюстрировать. Я закомментировал второй сервер и изменил код на вашем сервере.

void ThreadStart()
{
    if (!running)
    {
        listener.Start();
        running = true;
        lock (sync)
        {
            while (running)
            {
                try
                {
                    Console.WriteLine("BeginAccept [{0}]", 
                        Thread.CurrentThread.ManagedThreadId);
                    listener.BeginAcceptTcpClient(new AsyncCallback(Accept), listener);
                    Console.WriteLine("Wait [{0}]", 
                        Thread.CurrentThread.ManagedThreadId);
                    Monitor.Wait(sync);  // Release lock and wait for a pulse
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                }
            }
        }
    }
}

void Accept(IAsyncResult result)
{
    // Let the server continue listening
    lock (sync)
    {
        Console.WriteLine("Pulse [{0}]", 
            Thread.CurrentThread.ManagedThreadId);
        Monitor.Pulse(sync);
    }
    if (running)
    {
        TcpListener localListener = (TcpListener)result.AsyncState;
        using (TcpClient client = localListener.EndAcceptTcpClient(result))
        {
            handler.Handle(client.GetStream());
        }
    }
}

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

Press return to test...
BeginAccept [3]
Wait [3]

Press return to terminate...
Pulse [5]
BeginAccept [3]
Pulse [3]
Echo Handler: Test1
Echo Handler: Test3
Wait [3]

Как видите, вызываются два Pulse, один из отдельного потока (Pulse [5]), который пробуждает первый Wait. Затем поток 3 выполняет еще один BeginAccept, но, имея ожидающие входящие соединения, этот поток решает немедленно вызвать обратный вызов Accept. Поскольку Accept вызывается одним и тем же потоком, Lock (синхронизация) не блокируется, а сразу же Pulse [3] в пустой очереди потока.

Вызываются два обработчика, которые обрабатывают два сообщения.

Все в порядке. , и ThreadStart снова запускается и переходит в состояние ожидания на неопределенное время.

Теперь основная проблема заключается в том, что вы пытаетесь использовать монитор в качестве сигнала. Поскольку он не запоминает состояние, второй Pulse теряется.

Но для этого есть простое решение. Используйте AutoResetEvents, это правильный сигнал, и он запомнит свое состояние.

public Server(IHandler handler, int port)
{
    this.handler = handler;
    IPAddress address = Dns.GetHostEntry(Dns.GetHostName()).AddressList[0];
    listener = new TcpListener(address, port);
    running = false;
    _event = new AutoResetEvent(false);
}

public void Start()
{
    Thread thread = new Thread(ThreadStart);
    thread.Start();
}

public void Stop()
{
    listener.Stop();
    running = false;
    _event.Set();
}

void ThreadStart()
{
    if (!running)
    {
        listener.Start();
        running = true;
        while (running)
        {
            try
            {
                listener.BeginAcceptTcpClient(new AsyncCallback(Accept), listener);
                _event.WaitOne();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
    }
}

void Accept(IAsyncResult result)
{
    // Let the server continue listening
    _event.Set();
    if (running)
    {
        TcpListener localListener = (TcpListener) result.AsyncState;
        using (TcpClient client = localListener.EndAcceptTcpClient(result))
        {
            handler.Handle(client.GetStream());
        }
    }
}
5
ответ дан 5 December 2019 в 23:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: