Заполнение DataGridView из двух потоков
Возникла необходимость заполнить один столбец DataGridView несколькими потоками, причём задержка в каждом потоке разная. Если один поток уже заполнил ячейку то второй её пропускает и в ячейке отображается значение только из одного потока а если они одновременно заполняют ячейку тогда оба значения отображаются. Возможно ли в принципе это реализовать и если можно, то подскажите как. Таким образом я хочу смоделировать столкновение информационных кадров в локальной сети(лабораторная работа). У меня получается так что в ячейка заполняется из обоих потоков одновременно. Буду рад любой помощи, совету или критике.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
Random m_random = new Random((int)DateTime.Now.Ticks);
ManualResetEvent m_stopThreadsEvent = new ManualResetEvent(false);
private void button1_Click(object sender, EventArgs e)
{
dataGridView1.RowCount = 10;
}
private void ThreadOne()
{
int n = m_random.Next(1000);
AppendText("1");
}
private void ThreadTwo()
{
int n = m_random.Next(1000);
AppendText("1");
}
delegate void AppendTextDelegate(string text);
private void AppendText(string text)
{
if (dataGridView1.InvokeRequired)
{
dataGridView1.Invoke(new AppendTextDelegate(this.AppendText), new object[] { text });
}
else
{
for (int i = 0; i < 10; i++)
{
dataGridView1.Rows[i].Cells[0].Value = dataGridView1.Rows[i].Cells[0].Value +=text;
}
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
m_stopThreadsEvent.Set();
}
private void button2_Click(object sender, EventArgs e)
{
dataGridView1.RowCount = 10;
Thread t1 = new Thread(new ThreadStart(ThreadOne));
Thread t2 = new Thread(new ThreadStart(ThreadTwo));
t1.Start();
Thread.Sleep(300);
t2.Start();
Thread.Sleep(600);
}
}
Ответы (1 шт):
Я привык работать с привязками данных, поэтому у меня в примере будут привязки. Я буду использовать BindingList<T> и реализацию INotifyPropertyChanged.
На форме есть самая обычная Button и самая обычная DataGridView с автогенерацией колонок.
Создам класс, который будет представлять собой строчку таблицы. И обеспечу потокобезопасность при доступе к его свойству, а так же сделаю вызов события PropertyChanged в UI потоке. Чтобы на 100% быть уверенным, что "чудеса" происходят не по вине рассинхрона при обращении к данным из нескольких потоков.
public class Data : INotifyPropertyChanged
{
private object _syncRoot = new object();
private string _value = "";
private Form _form;
// если включена автогенерация колонок, заголовок колонки берется отсюда
[DisplayName("Abonent1")]
public string Value
{
get
{
lock (_syncRoot)
{
return _value;
}
}
set
{
lock (_syncRoot)
{
_value = value;
}
_form.Invoke((Action)(() => OnPropertyChanged()));
}
}
public Data(Form form)
{
_form = form;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
И реализую сам пример
public partial class Form1 : Form, INotifyPropertyChanged
{
private const int _count = 10;
private BindingList<Data> _items;
public BindingList<Data> Items
{
get => _items;
set
{
_items = value;
OnPropertyChanged();
}
}
public Form1()
{
InitializeComponent();
dataGridView1.DataBindings.Add("DataSource", this, nameof(Items));
}
private void Form1_Load(object sender, EventArgs e)
{
Items = new BindingList<Data>();
for (int i = 0; i < _count; i++)
{
Items.Add(new Data(this));
}
}
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < _count; i++)
Items[i].Value = "";
Thread t1 = new Thread(ThreadOne);
Thread t2 = new Thread(ThreadTwo);
t1.Start();
t2.Start();
}
private void ThreadOne()
{
for (int i = 0; i < _count; i++)
AppendText(i, "1");
}
private void ThreadTwo()
{
for (int i = 0; i < _count; i++)
AppendText(i, "2");
}
private void AppendText(int position, string text)
{
string s = Items[position].Value;
Thread.Sleep(1); // <--- вот здесь происходит столкновение потоков
// к моменту выполнения следующей строчки, переменная 's' и 'Items[position].Value'
// могут иметь разное значение
if (s == "")
Items[position].Value += text;
Thread.Sleep(10);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Ну и при нажатии на кнопку получаю следующий вывод в DataGridView:

