Как настроить синхронизацию потоков C#?

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

В приложение по примеру № 3 добавить вертикальную область. Внутри области может находиться только один шарик, другие шарики ожидают, пока область не освободится. Применить синхронизацию потоков.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace WindowsFormsApp2
{
    public partial class Form1 : Form
    {
        class Ball
        {
            static object locker = new object();
            public int x, y; // координаты
            int dx, dy; //приращение координат-определяет скорость
            int w, h; //ширина высота шарика
            public bool live = true; // признак жизни
            public delegate void DlTp();// Объявление типа (делегат) и
                                        //создание пока что пустой ссылки для организации в последующем
                                        // с помощью ее вызова функции Invalidate()для главного потока
            public DlTp dl;
            public Thread thr; //Создание ссылки на потоковый объект
                               // потоковая функция
            public void FnThr()
            {
                    while (live)
                    { //здесь отражемся от границ области
                        if (x < 0 || x > 200) dx = -dx;
                        if (y < 0 || y > 200) dy = -dy;
                        //здесь пересчитываем координаты
                        x += dx;
                        y += dy;
                        Thread.Sleep(30);//спим
                        dl(); //вызываем с помощью делегата Invalidate()
                    }
                w = h = 0; //схлопываем шарик
                dl(); //вызываем с помощью делегата Invalidate()
            }
            //функция рисования шарика
            public void DrawBall(Graphics dc)
            {
                dc.DrawEllipse(Pens.Magenta, x, y, w, h);
            }
            //конструктор класса
            public Ball(int xn, int yn, int wn, int hn, int dxn, int dyn)
            {
                x = xn; y = yn; w = wn; h = hn; dx = dxn; dy = dyn;//инициализируем
                thr = new Thread(new ThreadStart(FnThr)); //создаем потоковый объект
                live = true; //устанавливаем признак жизни
                thr.Start(); //запускаем поток
            }
        }
        Ball[] bl = new Ball[10];//массив пустых ссылок типа Ball
        public Form1()
        {
            InitializeComponent();
            for (int j = 0; j < bl.Length; j++)
            {
                //создаем потоковые объекты
                bl[j] = new Ball(j, j * 10, 10, 10, j + 1, j + 1);
                //подписываемся на событие
                bl[j].dl += new Ball.DlTp(Invalidate);
            }
        }
        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            for (int j = 0; j < bl.Length; j++)
            {
                bl[j].DrawBall(e.Graphics);//рисуем
               
                if(bl[j].x > 200)
                {
                    
                    for (int i = 0; i < bl.Length; i++)
                    {
                        if (bl[i] != bl[j])
                        {
                            lock (typeof(Ball)) bl[i].FnThr();
                            Console.WriteLine(bl[j].thr.ThreadState);
                        }
                    }
                }
            }
        }
        private void button1_Click(object sender, EventArgs e)
        {
            for (int j = 0; j < bl.Length; j++)
            {
                bl[j].live = false;// Уничтожаем потоки
            }
        }
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            for (int j = 0; j < bl.Length; j++)
            {
                bl[j].live = false;//уничтожаем потоки
            }
        }
    }
}


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

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

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

Вот так

public class Ball
{
}

public partial class Form1 : Form
{
}

В идеале эти классы нужно расположить в разных C# файлах.

Решение простое.

private void Form1_Paint(object sender, PaintEventArgs e)
{
    for (int j = 0; j < bl.Length; j++)
    {
        if (bl[j].live) // если неживой, игнорируем его
        {
            if(bl[j].x > 200) // если пересек
                bl[j].live = false; // убиваем
            else
                bl[j].DrawBall(e.Graphics); // иначе рисуем
        }
    }
}

Вместо пары x,y используйте структуру Point

public class Ball
{
    public Point Position;
}

Избегайте сокращений, код из непонятно почему так названных переменных (FnThr => RunLoop) очень сложно читать.

Вот, смотрите разницу:

private void Form1_Paint(object sender, PaintEventArgs e)
{
    for (int j = 0; j < balls.Length; j++)
    {
        if (balls[j].IsLive)
        {
            if(balls[j].Position.X > 200)
                balls[j].IsLive = false;
            else
                balls[j].Draw(e.Graphics);
        }
    }
}

Не экономьте буквы, от этого код быстрее работать не будет.

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

public bool IsActive;

public void RunLoop()
{
    while (IsLive)
    {
        if (x < 0 || x > 200) dx = -dx;
        if (y < 0 || y > 200) dy = -dy;
        
        x += dx;
        y += dy;
        do
        {
            Thread.Sleep(30); // спим
        } while (!IsActive); // пока деактивирован
        OnPositionChanged();
    }
    w = h = 0;
    OnPositionChanged();
}

Или даже так

private ManualResetEventSlim _activeLock = new ManualResetEventSlim(true);

public void RunLoop()
{
    while (IsLive)
    {
        if (x < 0 || x > 200) dx = -dx;
        if (y < 0 || y > 200) dy = -dy;
        
        x += dx;
        y += dy;
        Thread.Sleep(30); // спим
        _activeLock.Wait();
        OnPositionChanged();
    }
    w = h = 0;
    OnPositionChanged();
}

public void Deactivate()
{
    _activeLock.Reset();
}

public void Activate()
{
    _activeLock.Set();
}
→ Ссылка