Я пытался понять поток данных TPL, создав образец приложения. Одна из вещей, которые я пытался сделать, - это обновить элемент управления TextBox
от ActionBlock
. Причиной использования потока данных TPL является параллельная асинхронная работа при сохранении порядка. Следующая функция написана мной,Обновление контроля пользовательского интерфейса из ActionBlock
private TaskScheduler scheduler = null;
public Form1()
{
this.scheduler = TaskScheduler.FromCurrentSynchronizationContext();
InitializeComponent();
}
public async void btnTPLDataFlow_Click(object sender, EventArgs e)
{
Stopwatch watch = new Stopwatch();
watch.Start();
txtOutput.Clear();
ExecutionDataflowBlockOptions execOptions = new ExecutionDataflowBlockOptions();
execOptions.MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded;
execOptions.TaskScheduler = scheduler;
ActionBlock<int> actionBlock = new ActionBlock<int>(async v =>
{
bool x = await InsertIntoDatabaseAsync(v);
if (x)
txtOutput.Text += "Value Inserted for: " + v + Environment.NewLine;
else
txtOutput.Text += "Value Failed for: " + v + Environment.NewLine;
}, execOptions);
for (int i = 1; i <= 200; i++)
{
actionBlock.Post(i);
}
actionBlock.Complete();
await actionBlock.Completion;
watch.Stop();
lblTPLDataFlow.Text = Convert.ToString(watch.ElapsedMilliseconds/1000);
}
private async Task<bool> InsertIntoDatabaseAsync(int id)
{
try
{
string connString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=D:\\TPLDatabase.accdb;Persist Security Info=False;";
using (OleDbConnection conn = new OleDbConnection(connString))
{
string commandText = "INSERT INTO tblRecords (ProductName, ProductDescription, IsProcessed) VALUES (@ProductName, @ProductDescription, @IsProcessed)";
await conn.OpenAsync();
using (OleDbCommand command = new OleDbCommand(commandText, conn))
{
command.CommandType = CommandType.Text;
command.Parameters.AddWithValue("@ProductName", "Product " + id);
command.Parameters.AddWithValue("@ProductDescription", "Description " + id);
command.Parameters.AddWithValue("@IsProcessed", false);
if (await command.ExecuteNonQueryAsync() > 0)
return true;
else
return false;
}
}
}
catch
{
return false;
}
}
Теперь приведенная выше часть кода работает очень хорошо. Он вставляет записи по порядку в мою примерную базу данных MS Access и также обновляет пользовательский интерфейс. Но проблема заключается в том, что он блокирует пользовательский интерфейс, который является понятным, поскольку я использую TaskScheduler.FromCurrentSynchronizationContext
, который будет обновлять TextBox
на тему пользовательского интерфейса.
Я сделал небольшое изменение в коде и удалю планировщик с ExecutionDataflowBlockOptions
. Вместо этого я обновить пользовательский интерфейс с помощью следующего кода,
txtOutput.Invoke(new MethodInvoker(delegate
{
if (x)
txtOutput.Text += "Value Inserted for: " + v + Environment.NewLine;
else
txtOutput.Text += "Value Failed for: " + v + Environment.NewLine;
}));
Теперь это изменение не замерзает UI, но порядок значений в базе данных и порядок значений, отображаемых в TextBox нарушается плохо. Новый порядок, как это,
ID ProductName ProductDescription IsProcessed
6847 Product 6 Description 6 False
6848 Product 7 Description 7 False
6849 Product 8 Description 8 False
6850 Product 10 Description 10 False
6851 Product 11 Description 11 False
6852 Product 12 Description 12 False
6853 Product 9 Description 9 False
6854 Product 13 Description 13 False
6855 Product 14 Description 14 False
Теперь, что может быть лучшим способом для обновления пользовательского интерфейса в моем сценарии, а также поддержание порядка.
Великого. Благодарим вас за ответ и предложили способ справиться с этим сценарием. Поскольку я новичок в этом, так просто любопытно, что в чем преимущество TransformBlock над ActionBlock. (Я думаю, что два параметра означает один для ввода и один для вывода. Я прав? Также мне нужно дождаться завершения TransformBlock или ActionBlock? Наконец, самое главное, если исключение происходит в любом из блоков, и если я бросаю исключение, то будут ли все блоки прекращены? –
Выгода заключается в том, чтобы разделить работу на 2 блока. И так как вам нужно связать их, вам нужно сначала «TrasnformBlock». – i3arnon
Да, первый тип - это входной и второй это вывод. – i3arnon