Не дать запустить поток дважды

а если просто for, то нет ошибки.

Если проблема только в том, что структура копируется, то я не вижу в этом проблемы. Я изначально хотел занулять thread именно в массиве, а не в переданной куда-то структуре.

|Ошибка|CS0573|'"Form1.MyStruct": в структуре не могут содержаться инициализаторы свойств или полей экземпляров

фенита ля комедия

Так а зачем усложнять себе жизнь, сегодня в массиве, а завтра захочется передать куда-то, создать переменную, отфильтровать

using System;
using System.Linq;
using System.Threading;

public class Program
{
    struct Data
    {
        public int v;
        public Thread thread;
    }

    public static void Main()
    {
        var arr = new Data[10];

        arr[6].thread = new Thread(() => { });

        var notStartedTasks = arr.Where(it => it.thread == null).ToArray();

        Console.WriteLine($"Starting {notStartedTasks.Length} threads.");

        for (int i = 0; i < notStartedTasks.Length; i++)
        {
            notStartedTasks[i].thread = new Thread(() => { });
            notStartedTasks[i].v = 42;
        }

        Console.WriteLine(arr[0].v); // 0
    }
}

или что-нибудь еще, и будет непонятный баг.

Ну и без того же foreach неудобно, проще ошибиться в цикле.

В C# лучше по умолчанию использовать классы, о структурах думать только если объект соответствует критериям по ссылке выше (неизменяемость, маленький размер, похож на примитивные типы, …).

Ок, я уже понял, что надо использовать классы. Но меня напрягает то, что у классов нет метода уничтожения (если класс не имплементирует IDisposable). Но даже в этом случае, если я правильно понял, уничтожением класса занимается сборщик мусора когда ему вздумается.

Это другое, для освобождения ресурсов типа хэндлов WinApi, COM.

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

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

        private void ThreadWatchTerminate(object sender)
        {
            for (int i = 0; i < watchArr.Length; i++)
            {
                if (watchArr[i].threadWatch != null && watchArr[i].threadWatch.Equals(sender))
                {
                    watchArr[i].threadWatch = null;
                    MessageBox.Show(i.ToString());
                }
            }
        }

вроде работает. В боевых условиях еще не тестировал.

можно сразу