C#线程和断言不工作时出现问题

C#线程和断言不工作时出现问题,c#,multithreading,C#,Multithreading,我试图解决程序中的一个问题,但无法完成。我遇到问题的部分是修饰符“leu_solde_du_compte_a_partir_de_plusieurs_threads() 当我尝试使用Decimal Solde返回变量时,它会返回一个随机值,而不是0。断言中返回的值必须等于0。请帮帮我 这是Visual Studio C#中的代码 谢谢你 using System; public class CompteBancaire { public decimal solde1; publ

我试图解决程序中的一个问题,但无法完成。我遇到问题的部分是修饰符“leu_solde_du_compte_a_partir_de_plusieurs_threads()

当我尝试使用Decimal Solde返回变量时,它会返回一个随机值,而不是0。断言中返回的值必须等于0。请帮帮我

这是Visual Studio C#中的代码

谢谢你

using System;

public class CompteBancaire {
    public decimal solde1;
    public bool bool_fermer = true;

    public void Ouvrir()
    {
        solde1 = 0;
    }

    public void Fermer()
    {
        if (solde1 <= 0)
        {
            bool_fermer = false;
        }
    }

    public decimal Solde
    {

        get
        {
            if (!bool_fermer)
            {
                throw new InvalidOperationException("");
            }
            return solde1;
        }
    }

    /// <summary>
    /// Mettre a jour le solde du compte bancaire.
    /// </summary>
    public void ReviserSolde(decimal change)
    {
        solde1 += change;
    }
}

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xunit;

public class CompteBancaireTests
{
    [Fact]
    public void Retourne_un_solde_vide_apres_ouverture()
    {
        var compte = new CompteBancaire();
        compte.Ouvrir();

        Assert.Equal(0, compte.Solde);
    }

    [Fact]
    public void Verifier_le_solde()
    {
        var compte = new CompteBancaire();
        compte.Ouvrir();

        var solde_a_l_ouverture = compte.Solde;

        compte.ReviserSolde(10);
        var solde_mis_a_jour = compte.Solde;

        Assert.Equal(0, solde_a_l_ouverture);
        Assert.Equal(10, solde_mis_a_jour);
    }

    [Theory]
    [InlineData(10)]
    [InlineData(20)]
    public void Verifier_le_solde_random(int seed)
    {
        var compte = new CompteBancaire();
        compte.Ouvrir();

        var solde_a_l_ouverture = compte.Solde;

        var random = new Random(seed).Next(0, 100);

        compte.ReviserSolde(random);
        var solde_mis_a_jour = compte.Solde;

        Assert.Equal(0, solde_a_l_ouverture);
        Assert.Equal(random, solde_mis_a_jour);
    }

    [Fact]
    public void Le_solde_peut_augmenter_et_diminuer()
    {
        var compte = new CompteBancaire();
        compte.Ouvrir();
        var solde_a_l_ouverture = compte.Solde;

        compte.ReviserSolde(10);
        var ajouter_au_solde = compte.Solde;

        compte.ReviserSolde(-15);
        var soustraction_au_solde = compte.Solde;

        Assert.Equal(0, solde_a_l_ouverture);
        Assert.Equal(10, ajouter_au_solde);
        Assert.Equal(-5, soustraction_au_solde);
    }

    [Fact]
    public void Un_compte_ferme_lance_une_exception_lors_de_la_verification_du_solde()
    {
        var compte = new CompteBancaire();
        compte.Ouvrir();
        compte.Fermer();

        Assert.Throws<InvalidOperationException>(() => compte.Solde);
    }

    [Fact]
    public void Modifier_le_solde_du_compte_a_partir_de_plusieurs_threads()
    {
        var compte = new CompteBancaire();
        var liste_de_tasks = new List<Task>();

        const int nombre_de_threads = 500;
        const int iterations = 100;

        compte.Ouvrir();

        for (int i = 0; i < nombre_de_threads; i++)
        {
            liste_de_tasks.Add(Task.Run(() =>
            {
                for (int j = 0; j < iterations; j++)
                {
                    compte.ReviserSolde(1);
                    compte.ReviserSolde(-1);
                }
            }));
        }

        Task.WaitAll(liste_de_tasks.ToArray());

        Assert.Equal(0, compte.Solde);
    }
}
使用系统;
公营公司{
公共十进制solde1;
公共bool bool_fermer=true;
公共空间
{
solde1=0;
}
公开作废费默()
{
if(焊料1完整焊料);
}
[事实]
公共无效修饰符\u le_solde_du_compte_a_partir_de_plusieurs_threads()
{
var compte=新的CompteBancaire();
var liste_de_tasks=新列表();
const int nombre_de_线程=500;
常数int迭代=100;
compte.Ouvrir();
对于(int i=0;i
{
对于(int j=0;j
ReviserSolde
中使用的操作
+=
不是原子操作,因为它由多个线程调用,所以需要一些同步。例如,您可以修改单元测试,以便调用者处理它:

object locker = new object(); // create lock object
...

for (int i = 0; i < nombre_de_threads; i++)
{
    liste_de_tasks.Add(Task.Run(() =>
    {
        for (int j = 0; j < iterations; j++)
        {
            lock (locker) // lock the modification
            {
                compte.ReviserSolde(1);
                compte.ReviserSolde(-1);
            }
        }
    }));
}
object locker=new object();//创建锁对象
...
对于(int i=0;i
{
对于(int j=0;j
或者您可以使
ReviserSolde
线程安全(例如在那里添加
lock


为了更深入地探讨这个话题,我通常推荐Albahari的。

编辑:OP,我的答案——正如斯特隆大师所指出的——不是你想要的。为了让它在没有锁的情况下工作,它必须变得非常粗糙,而现在,它违背了多线程的目的

您的第一个for循环将在垃圾邮件线程/任务完成之前完成。这让我想到,在某个时刻,您的Task.WaitAll将发现没有剩下任何任务(即使您打算进一步迭代)

对我有效的是移动Task.WaitAll(liste_de_tasks.ToArray());进入第一个for循环,如下所示:

for (int i = 0; i < nombre_de_threads; i++)
        {
            liste_de_tasks.Add(Task.Run(() =>
            {
                for (int j = 0; j < iterations; j++)
                {
                    compte.ReviserSolde(1);
                    compte.ReviserSolde(-1);
                    
                }
            }));
            Task.WaitAll(liste_de_tasks.ToArray());
        }
for(int i=0;i
{
对于(int j=0;j
,例如@GuruStron
solde1
是一个
十进制
,但尽管不能保证线程安全,但考虑到在
任务之后预期会有输出,我不明白哪里会出现同步问题。WaitAll(…)
@Martin多线程(请参阅添加到
liste_de_任务
)正在通过
ReviserSolde
修改
compte
的同一个实例(
+=
不是一个原子操作),因此某些地方需要一些同步。我认为应该由“客户机”完成,即在嵌套循环中。@GuruStron是的,我完全理解代码。但是,该操作是使用500个任务执行的,每个任务加上和减去100次。如果您等待所有任务完成,
0
@Martin再次,
+=
不是原子的,那么最终结果将是相同的,例如,两个线程可以同时读取当前值(例如
0
),然后添加
1
,并将结果
1
放在那里,在添加2次后,在
solde
中给出
1
。然后在减法过程中,不会发生这样的重叠,导致最后出现
-1
。这将按顺序等待每个任务,基本上消除所有并行性,从而达到创建所有任务的目的,等等。您仍然会获得“一些”并行性,只是大大降低了。如果需要的话,你可以每100次做一次,但我明白你的意思。不,你不会。第一个创建的任务将立即等待,然后是第二个,依此类推。正如@guruston所指出的,这个答案完全没有意义。不要使用此解决方案。