C# 从C中的多个异步方法(即线程)中增加值类型#

C# 从C中的多个异步方法(即线程)中增加值类型#,c#,multithreading,asynchronous,async-await,C#,Multithreading,Asynchronous,Async Await,我需要在C#中增加多个线程(异步方法)的计数器 我无法传递ref参数,因为它是异步方法 我不能仅仅更新值(或锁定它),因为它是一种值类型 我不能使用Interlocked,因为我不能在async方法中对计数器进行引用 所以我唯一想做的就是做一些傻事,比如列表,然后把我的int放在里面,这样线程就可以锁定列表并更新值 我希望这是一个已知的用例,并且有一种更优雅的方式来实现它 下面是一个小示例,不要管小语法问题: public void DoStuff() { int counter;

我需要在C#中增加多个线程(异步方法)的计数器

  • 我无法传递ref参数,因为它是异步方法
  • 我不能仅仅更新值(或锁定它),因为它是一种值类型
  • 我不能使用Interlocked,因为我不能在async方法中对计数器进行引用
所以我唯一想做的就是做一些傻事,比如
列表
,然后把我的
int
放在里面,这样线程就可以锁定列表并更新值

我希望这是一个已知的用例,并且有一种更优雅的方式来实现它

下面是一个小示例,不要管小语法问题:

public void DoStuff()
{
    int counter;
    var tasks = new List<Task>()
    for(int i = 0; i < 10; i++)
    {
        tasks.Add(AsyncMethod(<SOMETHING>));
    }
    Task.WaitAll(tasks);
    Console.WriteLine("Total: {0}", counter);
 }

 public async Task AsyncMethod(<SOMETHING>)
 {
     // Lock if needed by <SOMETHING>
     <SOMETHING>+=20;
 }
public void DoStuff()
{
整数计数器;
var tasks=新列表()
对于(int i=0;i<10;i++)
{
tasks.Add(AsyncMethod());
}
Task.WaitAll(任务);
WriteLine(“总计:{0}”,计数器);
}
公共异步任务AsyncMethod()
{
//如果需要,请锁定
+=20;
}
我需要创建一个带有
int
字段的类,还是C#提供一些现成的东西?我并没有被困在这一点上,只是想事后看看是否有更好的方法。谢谢

对于未来的访客: 共识似乎是创建一个自定义类,如
class IntHolder{public int Value{get;set;}}
,可以通过引用传递并锁定(或使用Interlocked-on)


谢谢大家

您可以在任何对象上使用
锁定
,而不仅仅是要使用的对象

例如:

object locking_object = new object();
这将创建一个仅用于锁定的对象

然后,当您要增加值时:

lock(locking_object)
{
    integer++;
}
根据评论更新:

创建一个类来保存整数值,如下所示:

class IntHolder
{
    public int Value;
}
Interlocked.Increment(ref int_holder.Value);
您可以使用
Interlocked
类执行以下操作:

class IntHolder
{
    public int Value;
}
Interlocked.Increment(ref int_holder.Value);

其中,
int\u holder
是传递给方法的类型为
int holder
的变量的名称。

如果希望能够在异步方法之间传递值,或许可以利用:

private asynchlocal counter=new asynchlocal();
公共异步任务fooancy()
{
等待任务;
联锁增量(参考计数器值);
}

我想发布一个完整的代码示例b/c有些事情可能很棘手

    public class MyIntIncrementer
    {
        public int MyInt = 0;
    }

    public static String TimeStamp
    {
        get { return DateTime.UtcNow.ToString("HH:mm:ss.fff"); } //yyyy-MM-dd
    }

    public static void Main(string[] args)
    {
        List<Task<string>> tasks = new List<Task<string>>();
        int waitSeconds = 5;

        Console.WriteLine(String.Format("{0}: Start", TimeStamp));
        DateTime start = DateTime.Now;

        MyIntIncrementer iIncrementer = new MyIntIncrementer();
        iIncrementer.MyInt = 0;

        for (int i = 0; i < 10; i++)
        {
            //definitely loops and changes values - but when passed in to the function they don't remain that way... see iParam
            //Console.WriteLine(String.Format("{0}: Looping... i: {1}\n", TimeStamp,i));

            tasks.Add(Task.Run(() =>
            {
                // all have 10 => last value :(
                // Console.WriteLine(String.Format("{0}: Running... i: {1}\n", TimeStamp, i));

                return SayYesIfEven(waitSeconds, i, iIncrementer);
            }));
        }

        Console.WriteLine(String.Format("{0}: Before Wait...", TimeStamp));

        // wait for them to run
        Task.WhenAll(tasks).Wait();
        //Task.WhenAll(tasks); // doesn't wait with .Wait()

        Console.WriteLine(String.Format("{0}: After Wait... Results:", TimeStamp));

        // get the results
        for (int i = 0; i < tasks.Count; i++)
        {
            Console.WriteLine(tasks[i].Result);
        }

        Console.WriteLine(String.Format("{0}: Done  ({1}s)", TimeStamp, (DateTime.Now - start).TotalSeconds));
    }

    public static async Task<string> SayYesIfEven(int waitSeconds, int iParam, MyIntIncrementer iIncrementer)
    {
        int localIParamStart = (int)iParam; // no difference from passed in value when copied locally

        int currentIStart = iIncrementer.MyInt; // not guaranteed to be unique

        // iParam is the last value and when 'iIncrementer.MyInt' prints here, it's sometimes the same in multiple threads
        Console.WriteLine(String.Format("{0:00}: Before Increment: even? {1} <=> {2:00} / iP: {3:00} / LiP: {4:00} / in.mP: {5:00}", TimeStamp, (currentIStart % 2 == 0 ? "Yes" : "No "), currentIStart, iParam, localIParamStart, iIncrementer.MyInt));

        // best way to get a unique value 
        int currentIR = Interlocked.Increment(ref iIncrementer.MyInt); // all threads wait on a lock to increment and then they move forward with their own values
        int currentI = iIncrementer.MyInt;
        int localIParam = (int)iParam;

        Console.WriteLine(String.Format("{0:00}: After  Increment: even? {1} <=> {2:00} => {6:00} => {7:00} / iP: {3:00} / LiP: {4:00} => {8:00} / in.mP: {5:00}", TimeStamp, (currentI % 2 == 0 ? "Yes" : "No "), currentIStart, iParam, localIParamStart, iIncrementer.MyInt, currentIR, currentI, localIParam));

        await Task.Delay(waitSeconds * 1000); // simulate delay

        await Task.Run(() =>
        {
            // do other stuff...

            // iParam and iIncrementer.value have the last value (note that this statement runs after the above delay)
            Console.WriteLine(String.Format("{0:00}: Inside Run after Delay: even? {1} <=> {2:00} => {6:00} => {7:00} / iP: {3:00} / LiP: {4:00} => {8:00} / in.mP: {5:00}", TimeStamp, (currentI % 2 == 0 ? "Yes" : "No "), currentIStart, iParam, localIParamStart, iIncrementer.MyInt, currentIR, currentI, localIParam));
            return "something";
        });

        // all have last value when showing what was passed into SayYesIfEven - and iIncrementer.value is also the last value
        return (String.Format("{0:00}: Returning: even? {1} <=> {2:00} => {6:00} => {7:00} / iP: {3:00} / LiP: {4:00} => {8:00} / in.mP: {5:00}", TimeStamp, (currentI % 2 == 0 ? "Yes" : "No "), currentIStart, iParam, localIParamStart, iIncrementer.MyInt, currentIR, currentI, localIParam));
    }
公共类MyIntIncrementer
{
公共int-MyInt=0;
}
公共静态字符串时间戳
{
获取{return DateTime.UtcNow.ToString(“HH:mm:ss.fff”);}//yyy-mm-dd
}
公共静态void Main(字符串[]args)
{
列表任务=新列表();
整数秒=5;
WriteLine(String.Format(“{0}:Start”,TimeStamp));
DateTime start=DateTime.Now;
MyIntIncrementer IIIncrementer=新的MyIntIncrementer();
iIncrementer.MyInt=0;
对于(int i=0;i<10;i++)
{
//肯定会循环和更改值-但当传递到函数时,它们不会保持这种方式…请参阅iParam
//WriteLine(String.Format(“{0}:Looping…i:{1}\n”,TimeStamp,i));
tasks.Add(Task.Run)(()=>
{
//都有10=>最后一个值:(
//WriteLine(String.Format(“{0}:Running…i:{1}\n”,时间戳,i));
返回sayyesifen(等待秒、i、i增量);
}));
}
WriteLine(String.Format(“{0}:Before Wait…”,TimeStamp));
//等着他们跑
Task.WhenAll(tasks.Wait();
//Task.WhenAll(tasks);//不使用.wait()等待
WriteLine(String.Format(“{0}:After Wait…Results:”,TimeStamp));
//得到结果
for(int i=0;i{6:00}=>{7:00}/iP:{3:00}/LiP:{4:00}=>{8:00}/in.mP:{5:00}”,时间戳,(currentI%2==0?:“是”):“否”),currentIStart,iParam,localIParamStart,iIncrementer.MyInt,currentIR,currentI,localIParam);
等待任务。延迟(waitSeconds*1000);//模拟延迟
等待任务。运行(()=>
{
//做其他事情。。。
//IPRAM和IIIncrementer.value具有最后一个值(请注意,此语句在上述延迟之后运行)
Console.WriteLine(String.Format(“{0:00}:延迟后内部运行:偶数?{1}{2:00}=>{6:00}=>{7:00}/iP:{3:00}/LiP:{4:00}=>{8:00}/in.mP:{5:00}”,时间戳,(currentI%2==0?:“是”):“否”)、currentIStart、iParam、localIParamStart、iIncrementer.MyInt、currentIR、currentI、localIParam);
返回“某物”;
});
//当显示传递到sayyesifeen中的内容时,所有都具有最后一个值,并且iIncrementer.value也是t