c# - c# 线程和任务的并发错误
问题描述
描述
我有一个示例代码来执行线程并行在线登录。它需要尝试登录的次数Main
并将其传递给ParallelRun
. ParallelRun
除以longinAttemptsCount
线程数。然后它产生线程并传递一个线程 id 和每个线程的尝试次数ThreadAuthenticationTaskRunner
AuthenticateAsync
进行实际登录。
ThreadAuthenticationTaskRunner
打印出哪个线程正在启动,一旦完成,它就会打印出哪个线程已经结束。
预期成绩
我希望看到以任何顺序列出的线程,但我不应该看到重复的 ID。
实际结果
我看到一些线程 ID 缺失,而另一些则重复。我收到如下结果:
我在这里看到的这个并发错误是什么?
using System;
using System.Threading.Tasks;
using System.Threading;
using System.Collections.Generic;
namespace Stylelabs.M.WebSdk.Examples
{
public class Program
{
static void Main(string[] args)
{
int longinAttemptsCount = 1000;
Console.WriteLine("Main Thread Started");
//parallel run
ParallelRun(longinAttemptsCount);
Console.WriteLine("Main Thread Ended");
}
/// <summary>
/// Takes the number of required login attempts and spreads it across threads
/// </summary>
/// <param name="longinAttemptsCount"> Number of times to attempt to login</param>
static void ParallelRun(int longinAttemptsCount)
{
int numberOfLoginAttemptsPerThread = 100;
int numberOfThreads = longinAttemptsCount / numberOfLoginAttemptsPerThread;
Console.WriteLine("ParallelRun Started: " + numberOfThreads + " threads");
for (int i = 0; i < numberOfThreads; i++)
{
Thread thread1 = new Thread(() => ThreadAuthenticationTaskRunner(i, numberOfLoginAttemptsPerThread));
thread1.Start();
}
Console.WriteLine("ParallelRun Ended: " + numberOfThreads + " threads");
}
/// <summary>
/// Runs parallel logins for each thread
/// </summary>
/// <param name="threadId">The identifier of the running thread </param>
/// <param name="longinAttemptsCount">Number of times to attempt to login </param>
static async void ThreadAuthenticationTaskRunner(int threadId, int longinAttemptsCount)
{
Console.WriteLine("ThreadAuthenticationTaskRunner start for thread: " + threadId);
string userName = "administrator"; //user to log in
List<Task<String>> loginAttemptsResultsTasks = new List<Task<String>>();
//Executing the parallel logins
for (int i = 0; i < longinAttemptsCount; i++)
{
loginAttemptsResultsTasks.Add(AuthenticateAsync(userName, i, threadId));
}
var loginAttemptsResults = await Task.WhenAll(loginAttemptsResultsTasks);
foreach (string login in loginAttemptsResults)
{
Console.WriteLine(login);
}
Console.WriteLine("ThreadAuthenticationTaskRunner end for thread: " + threadId);
}
/// <summary>
/// Conducts an asynchronous login on a QA tenant
/// </summary>
/// <param name="userName"> The user to be logged in </param>
/// <param name="loginId"> The login attempt identifier </param>
/// <param name="threadId"> The identifier of the running thread </param>
/// <returns></returns>
static async Task<String> AuthenticateAsync(String userName, int loginId, int threadId)
{
String result;
try
{
//some asynchronous login logic here
result = "Success: loginId: " + loginId + " threadId: " + threadId;
}
catch (Exception e)
{
result = "Failure: loginId: " + loginId + " threadId: " + threadId + " error: " + e;
}
return result;
}
}
}
解决方案
这里的问题是
() => ThreadAuthenticationTaskRunner(i, numberOfLoginAttemptsPerThread)
捕获对语言环境变量的引用i
。然后,当调用该函数时,它将锁定变量当前具有的值。现在出现的问题Thread.Start
可能在 lambda 被调用到循环继续之前返回,增加 for 的值i
,然后新的 thead 为其 id 读取错误的值。
@stefan alreasy 提到的简单解决方案是在循环中引入一个新变量,例如:
for (int i = 0; i < numberOfThreads; i++)
{
var tmp = i;
Thread thread1 = new Thread(() => ThreadAuthenticationTaskRunner(tmp, numberOfLoginAttemptsPerThread));
thread1.Start();
}
哇 lambda caputers 它是自己的tmp
变量,没有人会改变。请注意,每个循环迭代都会有自己的tmp
变量,并且它们将主要存在于堆上而不是堆栈上。
推荐阅读
- reactjs - 如何使用 useState 附加到对象进行无限滚动分页?
- node.js - 依赖冲突——节点
- python - 在 pyspark SQL 语句中传递变量
- gradle - 找不到对象上的参数 [...] 的方法 implementation()...DefaultDependencyHandler
- java - RecyclerView 项目覆盖整个屏幕
- html - hr颜色不清晰
- gnuplot - GNU 绘图和动画(在 Windows 上)
- html - 有没有办法将 8 列 div 推到右侧而不在 Bootstrap 中添加 4 列 div?
- c++ - Eigen3 A.ldlt().solve(b) 错误符号
- kotlin - 与 LiveData 观察者、适配器、协程混淆