Vali Maties Ответов: 1

Асинхронная/ожидающая проблема с результатом


Привет. У меня есть проблема с async/await, так как результат не всегда один и тот же.

Проблема в том, что файлы всегда одни и те же, но результаты разные. Иногда показывает мне результат, иногда другой результат.
Что я делаю не так?

Что я уже пробовал:

У меня есть метод в моей форме, который возвращает количество некоторых конкретных элементов из xml.

private int GetNumberOfMissingElements(string filePath)
{
    int i = 0;
    XDocument xml = XDocument.Load(filePath);
    foreach (XElement xe in xml.Descendants("someElement"))
    {
        if (xe.Descendants("someSpecificChild").Count() == 0)
        {
            i++;
        }
    }
    return i;
}


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

private async void CountTotalMissingElements(DataTable files)
{
    foreach(DataRow r in files.Rows)
    {
        string filePath = r["FilePath"].ToString();
        var result = await Task.Run(() => GetNumberOfMissingElements(filePath));
        TotalMissingElements += result;
    }
}


И моя собственность TotalMissingElements является:

private int totalMissingElements;
public int TotalMissingElements
{
    get => totalMissingElements;
    set
    {
        totalMissingElements = value;
        if(txtTME.InvokeRequired)
        {
            txtTME.BeginInvoke((MethodInvoker)delegate () 
            {
                txtTME.Text = totalMissingElements.ToString();
            }
        }
        else
        {
            txtTME.Text = totalMissingElements.ToString();
        }
    }
}



С уважением,
Вали

Gerry Schmitz

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

Vali Maties

Хорошо. Я сделал этот тест. Консольный текст я скопировал в csv и импортировал, отсортировал и сравнил в OpenOffice SCalc. Кажется, данные в порядке, но результат был другим. Практически, "TotalMissingElements += result;" не делает того, что должно! Почему?
Что я должен делать в этом случае?
Кстати: что означает "блокировать общие переменные"? :)

Vali Maties

Хорошо. Эта "блокировка" сделала мой день :D я провел некоторое исследование (я не так экспериментировал программистом в C#) и узнал, как использовать Interlock.Add()!

Спасибо тебе, Джерри!

Gerry Schmitz

Всегда пожалуйста! Рад, что вы поняли ссылку на "блокировку"; документы более интересны, чем я.

Tobynate

Вероятно, это не решение, но вы должны вернуть тип async Task при использовании await commant

1 Ответов

Рейтинг:
2

Richard Deeming

Один BackgroundWorker[^] вероятно, было бы лучше подходит для такого рода проблем.

private readonly BackgroundWorker _missingElementsWorker;
private int _totalMissingElements;

public Form1()
{
    InitializeComponent();
    
    _missingElementsWorker = new BackgroundWorker
    {
        WorkerReportsProgress = true,
        WorkerSupportsCancellation = true,
    };
    
    _missingElementsWorker.ProgressChanged += MissingElementsProgressChanged;
    _missingElementsWorker.DoWork += CountMissingElements;
    _missingElementsWorker.RunWorkerCompleted += MissingElementsCounted;
}

public int TotalMissingElements
{
    get { return _totalMissingElements; }
    set
    {
        _totalMissingElements = value;
        txtTME.Text = totalMissingElements.ToString();
    }
}

private void CountTotalMissingElements(DataTable files)
{
    TotalMissingElements = 0;
    _missingElementsWorker.RunWorkerAsync(files);
}

private void CountMissingElements(object sender, DoWorkEventArgs e)
{
    BackgroundWorker worker = (BackgroundWorker)sender;
    DataTable files = (DataTable)e.Argument;
    int result = 0;
    
    foreach (DataRow r in files.Rows)
    {
        if (worker.CancellationPending)
        {
            e.Cancel = true;
            return;
        }
        
        string filePath = r["FilePath"].ToString();
        result += GetNumberOfMissingElements(filePath);
        worker.ReportProgress(result);
    }
    
    e.Result = result;
}

private void MissingElementsProgressChanged(object sender, ProgressChangedEventArgs e)
{
    TotalMissingElements = e.ProgressPercentage;
}

private void MissingElementsCounted(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Cancelled)
    {
        // The count was cancelled...
    }
    else if (e.Error != null)
    {
        // An exception was thrown...
    }
    else
    {
        int totalMissingElements = (int)e.Result;
        Debug.Assert(totalMissingElements == TotalMissingElements);
    }
}


Vali Maties

Где это называется " CountTotalMissingElements` ?
Вероятно, в конструкторе, после `_missingElementsWorker.RunWorkerCompleted += MissingElementsCounted;` ?

Richard Deeming

Из того же места, куда вы сейчас звоните. Ты не показал этого в своем вопросе. :)

Vali Maties

Вот именно... хорошо... Я попробую эту версию BackgroundWorker...