Если опустить нудные подробности, то суть в следующем.
Есть массив структур типа:
public struct MyStruct
{
public string s1;
public string s2;
public MyThread thread = null;
}
Есть таймер, который проходит по массиву и для каждой структуры проверяет некое условие. Если это условие выполняется и поток еще не запущен - запускаем его.
Как при завершении потока узнать, какой именно структуре он принадлежал, чтобы там занулить thread?
Пока на ум приходит только следующее:
private void Thread_Complete(object sender)
{
пройтись по массиву и сравнить [b]sender[/b] с [b]thread[/b]
}
Cогласен. Можно использовать его. Но сейчас не имеет значения, что использовать.
Главное это
Разве это зависит от того, как был создан поток и, тем более, что он делает? Мы же сейчас про момент его завершения говорим. Какая разница, что он делал?
Он каждые пару секунд должен качать JSON и проверять наличие в нем определенного параметра. Если параметр начинает существовать - сохраняем его в файл и завершаем цикл. А если сам JSON перестаёт существовать - тоже выходим.
Плохо разбираюсь в терминологии. Я знаю, что такое “подписка”, но, по-моему, методы не могут ни на что подписаться. Так или иначе, этот метод повешан на завершение потока. Из названия же понятно.
Я просто спрашивал откуда это потому что в Thread вроде нет никаких событий завершения. И сам он ничего не синхронизирует, надо например вызывать Invoke / BeginInvoke, чтобы выполнить код в UI потоке (как Synchronize в Дельфи).
В BW есть, он похож на TThread из Дельфи. Данные там можно передать например через e.Result в DoWork и Completed.
Да, такая проблема может вообще не возникнуть с каким-нибудь async/await, или например с анонимными функциями (тогда не надо ничего искать, ссылка на структуру уже есть).
struct Data
{
public int d;
}
private void button1_Click(object sender, EventArgs e)
{
var data = new Data { d = new Random().Next() };
var thread = new Thread(() =>
{
Thread.Sleep(6000);
Invoke((Action)(() =>
{
MessageBox.Show($"Hi! {data.d}");
}));
});
thread.Start();
}
Этот поток и не должен синхронизироваться. Он просто долго выполняется и выходит. А после выхода (завершения потока), нужно узнать, какой структуре в массиве он принадлежал, чтобы там занулить его идентификатор, чтобы была возможность запустить этот поток заново.
Допустим, если использовать классический метод создания потока. Когда поток создаётся в отдельном классе. Без лямба-функций и прочего.
В вашем коде создаётся новый экземпляр структуры. Не понял, зачем? Как тогда найти соответствие в массиве?
struct Data
{
public int d;
}
List<Data> list = new List<Data>
{
new Data { d = 42 },
new Data { d = 43 },
};
private void button1_Click(object sender, EventArgs e)
{
int i = 0;
foreach (var data in list)
{
int ind = ++i;
var thread = new Thread(() =>
{
Thread.Sleep(2000 * ind);
Invoke((Action)(() =>
{
MessageBox.Show($"Hi from thread #{ind}! {data.d}");
}));
});
thread.Start();
}
}
Тут тогда получится, что два потока меняют как минимум это
хотя присваивание ссылки вроде бы atomic в .NET, так что наверно ок (смотря что именно делать), но скорее всего стоит хотя бы добавить volatile. https://stackoverflow.com/a/5209632/964478
Вы не поняли. В каждой структуре есть поле public MyThread thread = null. Таймер проходит по массиву и если условие == true && thread == null, то thread = new MyThread(...);.
А сами потоки ни с массивом, ни с формой, ни друг с другом никак не будут взаимодействовать и ничего нигде менять не будут.
Просто надо узнать, какой именно поток завершился. Ведь при втором и последующих срабатываниях таймера, thread будет уже не null и поток уже больше никогда не запустится, даже если он уже завершился.
А сами потоки ни с массивом, ни с формой, ни друг с другом никак не будут взаимодействовать и ничего нигде менять не будут
а null кто поместит в структуру при завершении потока? Все равно сам поток и должен при завершении установить значение какой-то переменной в этой структуре, не важно в событии или еще как, важно, что используя синхронизацию
Где вызвано, там и выполнится. Код внутри выполнится в основном потоке.
Invoke будет ждать перед продолжением выполнения потока пока оно выполнится, с BeginInvoke сразу продолжит не дожидаясь.
структуры — значения, а не ссылки.
using System;
public class Program
{
struct Data
{
public int v;
}
public static void Main()
{
Data d;
d.v = 42;
Console.WriteLine(d.v);
var d2 = d;
d.v = 43;
Console.WriteLine(d.v); // 43
Console.WriteLine(d2.v); // 42
}
}
using System;
public class Program
{
class Data
{
public int v;
}
public static void Main()
{
Data d = new Data();
d.v = 42;
Console.WriteLine(d.v);
var d2 = d;
d.v = 43;
Console.WriteLine(d.v); // 43
Console.WriteLine(d2.v); // 43
}
}
Так наткнуться можно как раз если это структура, а не класс.
Как советуют по ссылке выше, структуры стоит использовать только для неизменяемых объектов (без изменения полей после инициализации), похожих по использованию на примитивные типы (числа, строки).
Тут явно не тот случай раз надо менять поле структуры.
Поэтому тут легко можно допустить ошибку типа случайной передачи параметром и изменения копии, присвоения элемента массива/списка в переменную и т.д.
using System;
public class Program
{
struct Data
{
public int v;
}
public static void Main()
{
var arr = new Data[2];
foreach (var it in arr)
{
//it.v = 42; // ошибка компиляции
}
var item = arr[0];
item.v = 42;
Console.WriteLine(arr[0].v); // 0