【发布时间】:2020-04-21 10:06:08
【问题描述】:
说明
我有一个示例代码来执行线程并行在线登录。在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;
}
}
}
【问题讨论】:
-
似乎您遇到了
int i不是通过引用而不是通过值传递的问题。尝试将值复制到循环内的 tmp 变量:for (int i = 0; ....) { var copy = i; ..Runner(copy, ...) -
@stefan hmmm :) 确实如此。这是 C# 问题还是一般并发问题?谢谢!
-
它是您正在创建的闭包的详细信息 () => ThreadAuthenticationTaskRunner(i, numberOfLoginAttemptsPerThread) - 不确定它是特定于 c# 还是只是闭包标准实现的一般事物。不会认为这是一个问题,只是您需要了解的实现细节
-
附带说明,比起
Thread,更喜欢Task.Run。将async void与Thread一起使用可能会导致一些令人惊讶的行为。
标签: c# multithreading task