【问题标题】:Call method inside multiple tasks C#在多个任务中调用方法C#
【发布时间】:2020-04-30 08:43:54
【问题描述】:

我在调用任务列表中的方法时遇到问题。我有一种方法可以创建 N 个任务。在每个任务中,我都会执行一些操作,最终通过HttpWebRequest 获取数据并将该数据写入文件。我使用锁定对象来锁定对变量等共享资源的访问。除了调用创建一个执行HttpWebRequest(方法GetData)的方法之外,一切都执行得很好。每当我不锁定该方法的调用 (GetData) 时,似乎都会跳过一些数据/文件。例如:

  • 使用锁定对象,我得到文件 1、2、3 和 4
  • 没有锁定对象我得到文件 2,4 和 3

这是创建任务的方法的代码

private object lockObjectWebRequest= new object();
private object lockObjectTransactions = new object();

public List<Task> ExtractLoanTransactionsData(string URLReceived, string Headers, string Body)
{
    List<Task> Tasks = new List<Task>();

    try
    {
        int Limit = 0;
        int OffsetItemsTotal = 0;
        int NumberOftasks = 4;

        // Create the task to run in parallel
        for (int i = 0; i <= NumberOftasks; i++)
        {
            int OffsetCalculated = 0;

            if (i > 0)
            {
                OffsetCalculated = Limit * i;
            }

            Tasks.Add(Task.Factory.StartNew(() =>
            {
                string URL = URLReceived+ "&offset=" + OffsetCalculated .ToString() + "&limit=" + Limit.ToString();
                string Output = string.Empty;

                lock (lockObjectWebRequest)
                {
                    Output = GetData(URL, Headers,Body);
                }

                if (Output != "[]")
                {
                    lock (lockObjectTransactions)
                    {

                        Identifier++;
                        Job.Identifier = Identifier;

                        // write to file
                        string json = JValue.Parse(Output).ToString(Formatting.Indented);

                        string FileName = OffSet.ToString() + Identifier;
                        string Path = @"C:\FileFolder\" + FileName + ".json";

                        File.WriteAllText(Path, json);
                    }
                }

            }));
        }
    }
    catch (Exception ex)
    {
        Tasks = new List<Task>();
    }

    return Tasks;
}

这是执行HttpWebRequest的代码:

public string GetData(string URL, string Headers, string Body)
{
    string Data = string.Empty;
    Headers = Headers.Trim('{').Trim('}');
    string[] HeadersSplit = Headers.Split(new char[] { ',', ':' });

    HttpWebRequest WebRequest = (HttpWebRequest)HttpWebRequest.Create(URL);
    WebRequest.Credentials = new NetworkCredential();
    WebRequest.Method = "POST";

    HttpWebResponse WebResponse;

    // Set necessary Request Headers
    for (int i = 0; i < HeadersSplit.Length; i = i + 2)
    {
        string HeaderPart1 = HeadersSplit[i].Replace("\"", "").Trim();
        string HeaderPart2 = HeadersSplit[i + 1].Replace("\"", "").Trim();

        if (HeaderPart1 == "Content-Type")
        {
            WebRequest.ContentType = HeaderPart2;
        }
        else if (HeaderPart1 == "Accept")
        {
            WebRequest.Accept = HeaderPart2;
        }
        else if (HeaderPart1 == "Authorization")
        {
            WebRequest.Headers["Authorization"] = HeaderPart2;
        }
    }

    WebRequest.Headers.Add("cache-control", "no-cache");

    // Add body to Request
    using (var streamWriter = new StreamWriter(WebRequest.GetRequestStream()))
    {
        streamWriter.Write(Body);
        streamWriter.Flush();
        streamWriter.Close();
    }

     // Execute Request
     WebResponse = (HttpWebResponse)WebRequest.GetResponse();

     // Validate Response
     if (WebResponse.StatusCode == HttpStatusCode.OK)
     {
         using (var streamReader = new StreamReader(WebResponse.GetResponseStream()))
         {
             Data = streamReader.ReadToEnd();
         }
      }

     return Data;
}

我在这里做错了什么?该方法没有任务之间共享的全局数据。

【问题讨论】:

  • 首先请永远不要写catch (Exception ex)。这是一个糟糕的反模式。您应该只捕获您可以有意义地处理的特定异常。您应该始终防御性地编码以避免首先出现异常。
  • 您的问题中有很多代码。理想情况下,您应该尝试删除与问题无关的任何行,并保留仍然足以重现问题的绝对最小值。这将使您的问题更容易回答。
  • @TheodorZoulias 我认为显示所有代码以查看流程会更容易。我还包含了 GetData 方法的代码,因为当我调用此方法时,似乎会发生问题,所有创建的任务都没有锁定对象。
  • @AtomicBrownie - 看到不必要的东西并不容易。如果您必须在任务中添加lock,这似乎是完全错误的。锁定所有的 IO 是疯狂的。您需要提供minimal reproducible example
  • Maarten 的回答是我向您建议的。它应该使您能够删除所有锁。我看不到您需要锁定的任何其他共享资源。也就是说,您的代码中有未定义的变量。我无法复制、粘贴和编译您的代码。所以我不知道是否还有其他事情发生。你能提供一个minimal reproducible example吗?

标签: c# multithreading task


【解决方案1】:

但您确实有在任务之间共享的数据:本地变量Identifier 和方法参数Job

您正在使用文件名中的Identifier 写入文件。如果锁没有到位,那段代码将同时运行。

Job 的含义无法从您的问题中推断出来。

我认为您可以通过这样做来解决标识符问题:

var identifier = Interlocked.Increment(ref Identifier);
Job.Identifier = identifier; // Use 'identifier', not 'Identifier'

// write to file
string json = ...;

string FileName = OffSet.ToString() + "_" +
                  "MAMBU_LT_" + DateTime.Now.ToString("yyyyMMddHHmmss") + "_" +
                  identifier; // Use 'identifier', not 'Identifier'
...

【讨论】:

  • 变量标识符增量和文件名构造在不同的锁对象下。当我删除锁定对方法 GetData 的调用的锁定对象 lockObjectAccounts 时,问题就出现了。还是我正在使用的锁定对象没有锁定该变量增量?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-09-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-04
  • 2016-08-26
相关资源
最近更新 更多