无法识别线程循环中的并发

时间:2018-12-01 18:07:34

标签: c# multithreading

我很难理解此处的并发性以及排除锁的放置位置。

思想是为客户端知道的每台服务器创建一个线程。在编译时不知道服务器。客户端作为随时间变化的服务器的视图。每个线程(通过远程调用与服务器通信的线程)都放在taskArray上,该线程随后返回到调用函数,根据许多因素在该函数上执行WaitAll,WaitAny和其他类型的Wait。

问题是,当我有N台服务器时,说三台。所有这三个任务都调用了副本服务器上的第三台服务器,有时是第二台服务器。

如果我将睡眠代码放置在For循环中,则无论我正在运行多少台服务器,一切都会按预期运行。但是,像这样的硬编码从来都不是一个好主意,我正在寻找一个可以确切解释这里发生了什么(我怀疑)或者至少应该锁定哪些变量的人。因为我尝试在睡眠之前锁定的设备并没有解决问题。

我创建每个任务的代码是这样的:

    private Task<ReplyData>[] InitiateServerCalls(List<ServerData> replicasList, XuLiskovRequest request, CancellationToken cancellationToken)
    {
        int replicasCount = replicasList.Count;
        ServerData serverData;
        RequestData requestData;
        Task<ReplyData>[] tasksArray = new Task<ReplyData>[replicasCount];

        try
        {
            for (int ridx = 0; ridx < replicasCount; ridx++)
            {
                Thread.Sleep(250);
                serverData = replicasList[ridx];
                requestData = request.RequestData;
                tasksArray[ridx] = new Task<ReplyData>(() => CallServer(serverData, requestData, cancellationToken), cancellationToken);
                tasksArray[ridx].Start();
            }
        }
        catch (RemotingException) { RequestViewChange(); }

        return tasksArray;
    }

每个线程应运行的方法如下:

     private ReplyData CallServer(ServerData serverData, RequestData requestData, CancellationToken token)
    {
        string serverProxyURL;
        IServerService server_proxy;

        token.ThrowIfCancellationRequested();

        serverProxyURL = $"tcp://{serverData.ServerURL}/{serverData.ServerId}";
        Utils.Print($" [*] Asking {serverData.ServerId} to execute request...");
        server_proxy = (IServerService)Activator.GetObject(typeof(IServerService), serverProxyURL);

        return server_proxy.Request(requestData);
    }

1 个答案:

答案 0 :(得分:2)

serverDatarequestDat的值错误地被封闭在Task的lamda中。将变量的声明移入循环应该可以减轻这种情况

private Task<ReplyData>[] InitiateServerCalls(List<ServerData> replicasList, XuLiskovRequest request, CancellationToken cancellationToken)
{
    int replicasCount = replicasList.Count;
    Task<ReplyData>[] tasksArray = new Task<ReplyData>[replicasCount];

    try
    {
        for (int ridx = 0; ridx < replicasCount; ridx++)
        {
            Thread.Sleep(250);
            ServerData serverData = replicasList[ridx];
            RequestData requestData = request.RequestData;
            tasksArray[ridx] = new Task<ReplyData>(() => CallServer(serverData, requestData, cancellationToken), cancellationToken);
            tasksArray[ridx].Start();
        }
    }
    catch (RemotingException) { RequestViewChange(); }

    return tasksArray;
}