Click here to Skip to main content
15,038,345 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
Gerry Schmitz 25-Sep-20 13:13pm
   
Output each "result" to confirm whether parsing or adding is the problem. Maybe you need to "interlock" shared variables.
Vali Maties 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"? :)
Vali Maties 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!
Gerry Schmitz 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);
    }
}
   
Comments
Vali Maties 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. :)
Vali Maties 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