Семафоры и потоки c#

У меня есть бензозаправка,на которой 4 места,то есть максимум 4 машины одновременно могут заправляться.Мне надо посчитать,сколько машин проехали мимо.Машина подъезжает каждую минуту(в программе это будет секунда),заправляется 3 минуты(3 секунды). У меня не получается это сделать. Не знаю,как подловить момент,когда поток блокируется семафором.

Код:

class Task3
{
    class GasStation
    {

        static Semaphore sem = new Semaphore(4, 4);

        int space;
        int leave_count;
        public GasStation(int count)
        {
            leave_count = 0;
            space = 4;
            for (int i = 1; i < count + 1; i++)
            {
                Thread myThread = new Thread(Refuel);
                myThread.Name = $"Машина {i}";
                myThread.Start();


                Thread.Sleep(1000);
               // Console.WriteLine("Прошла 1 минута");
            }

            
        }
        public void Refuel()
        {
  
            while (space > 0)
            {

                if (sem.WaitOne())  // ожидаем, когда освободится место
                {
                    Console.WriteLine($"{} паркуется");

                    Console.WriteLine($"{Thread.CurrentThread.Name} заправляется бензином");

                    Thread.Sleep(3000);

                    //  Console.WriteLine("Прошло 3 минуты");

                    Console.WriteLine($"{Thread.CurrentThread.Name} выезжает с заправки");

                    Thread.Sleep(1000);

                    sem.Release();  // освобождаем место

                    space--;
                }
                else {
                    Console.WriteLine($"{Thread.CurrentThread.Name} проехала мимо");

                }
               
              

            }
        }

    }
    private static void Main()
    {
        GasStation gasStation = new GasStation(7);
    }
}

Ответы (1 шт):

Автор решения: aepot

Задача на знание семафора. Дело в том, что здесь считать ничего не надо кроме количества проехавших, семафор сам всё остальное посчитает, это его работа.

Так как по условию задачи не требуется очередь машин, то блокировать поток ожидание освобождения семафоране нужно. Вполне подойдет WaitOne(0), который ждёт 0 миллисекунд и возвращает true если место есть и false если нет. Еще проблема в том, что у вас заправка в том числе играет роль дороги, с которой машины приезжают, давайте отделим эти процессы.

Заправка

class GasStation
{
    private Semaphore semaphore;

    public GasStation(int capacity)
    {
        semaphore = new Semaphore(capacity, capacity);
    }

    private void Refuel()
    {
        Console.WriteLine($"{Thread.CurrentThread.Name} заправляется бензином");
        Thread.Sleep(4000);
        Console.WriteLine($"{Thread.CurrentThread.Name} уехала с заправки");
        semaphore.Release();
    }

    public bool StartRefuel(string name)
    {
        if (semaphore.WaitOne(0))
        {
            Thread thread = new Thread(Refuel);
            thread.Name = name;
            thread.Start();
            return true;
        }
        else
        {
            Console.WriteLine($"{name} проехала мимо");
            return false;
        }
    }
}

Поехали машины по вашим условиям

int stationCapacity = 4;
int carsCount = 7;
GasStation station = new GasStation(stationCapacity);
int leftCount = 0;
for (int i = 0; i < carsCount; i++)
{
    if (!station.StartRefuel($"Машина {i + 1}"))
        leftCount++;
    Thread.Sleep(1000);
}
Console.WriteLine($"Проехало мимо {leftCount} машин");

Получилось вот так

Машина 1 заправляется бензином
Машина 2 заправляется бензином
Машина 3 заправляется бензином
Машина 4 заправляется бензином
Машина 1 уехала с заправки
Машина 5 заправляется бензином
Машина 2 уехала с заправки
Машина 6 заправляется бензином
Машина 3 уехала с заправки
Машина 7 заправляется бензином
Машина 4 уехала с заправки
Проехало мимо 0 машин
Машина 5 уехала с заправки
Машина 6 уехала с заправки
Машина 7 уехала с заправки

При чем этот вывод при таких параметрах не всегда будет одинаковым, так как на стыке событий, когда пора запустить машину, место может успеть освободиться, может не успеть, так как запуск новой машины и выезд с заправки - практически одновременные события. То есть 1-2 машины могут мимо все-таки проехать, это нормальное явление, а не ошибка, там роль играет разница в +- несколько миллисекунд между выездом и заездом новой машины.

Вот другая попытка

Машина 1 заправляется бензином
Машина 2 заправляется бензином
Машина 3 заправляется бензином
Машина 4 заправляется бензином
Машина 5 проехала мимо
Машина 1 уехала с заправки
Машина 2 уехала с заправки
Машина 6 заправляется бензином
Машина 3 уехала с заправки
Машина 7 заправляется бензином
Машина 4 уехала с заправки
Проехало мимо 1 машин
Машина 6 уехала с заправки
Машина 7 уехала с заправки

Здесь машина 5 приехала раньше, чем машина 1 выехала.

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

→ Ссылка