Click here to Skip to main content
15,880,469 members
Please Sign up or sign in to vote.
2.50/5 (2 votes)
See more:
Hi. I have a problem with async/await, as the result is not the same every time.

The problem is the files are always the same, but results are different. Sometimes shows me a result, sometimes other result.
What I'm doing wrong?

What I have tried:

I have a method in my form which returns the count of some specific elements from an xml.

C#
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;
}


This method I want to use it in a click event of a button, in async way on multiple files. As long as I return an integer value from this method I want to increment a property on my form, which will be displayed in a text box on my form.

C#
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;
    }
}


And my TotalMissingElements property is:

C#
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();
        }
    }
}



Regards,
Vali
Posted
Updated 28-Sep-20 2:30am
Comments
[no name] 25-Sep-20 13:13pm    
Output each "result" to confirm whether parsing or adding is the problem. Maybe you need to "interlock" shared variables.
MVSoftVM 25-Sep-20 14:02pm    
Ok. I made this test. The console text I've copied to a csv and imported, sorted and compared in OpenOffice SCalc. Seems data is ok, but the result was different. Practically, `TotalMissingElements += result;` is not doing what it should! Why?
What I have to do in this case?
BTW: What means "interlock shared variables"? :)
MVSoftVM 25-Sep-20 15:25pm    
Ok. That "interlock" made my day :D I made some research (I'm not so experimented programmer in C#) and I learned how to use Interlock.Add()!

Thank you Gerry!
[no name] 26-Sep-20 13:52pm    
You're welcome! Glad you understood the "interlock" reference; the docs are more interesting than me.
Tobynate 25-Sep-20 23:16pm    
Probably not the solution, but you should return type async Task when using await commant

1 solution

A BackgroundWorker[^] would probably be a better fit for this type of problem.
C#
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);
    }
}
 
Share this answer
 
Comments
MVSoftVM 28-Sep-20 9:01am    
Where is called `CountTotalMissingElements` ?
Probably in constructor, after `_missingElementsWorker.RunWorkerCompleted += MissingElementsCounted;` ?
Richard Deeming 28-Sep-20 9:02am    
From the same place you're calling it at the moment. You didn't show that part in your question. :)
MVSoftVM 28-Sep-20 9:06am    
Exactly... ok... I will try this BackgroundWorker version...

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900