【问题标题】:C# asynchronous threading API crawlerC#异步线程API爬虫
【发布时间】:2019-12-09 14:29:50
【问题描述】:

我有一个程序可以从在线 API 发送/接收 POST 请求/响应,类似于买卖商品的市场机器人。它工作得很好,但是当我运行它时,它会锁定当前线程并且我无法在程序中使用其他任何东西。将来我还想让买卖异步,这样它们就可以同时发生。这是执行的代码,你可以看到它不断循环直到满足条件:

private void RunBot()
{
    numToBuy = (int)nudNumToBuy.Value;

    for (int i = 0; i < numToBuy; i++)
    {
        while (true)
        {
            if (AttachAndBuy())
            {
                break;
            }
        }
    }
}
private bool AttachAndBuy()
{
    string data = "<DATA HERE>";
    string URL = "<URL HERE>";

    Cookies.SetCookies(cookie, "PHPSESSID=" + SessionIDTextBox.Text.Replace("PHPSESSID=", ""));
    HttpWebRequest Request = (HttpWebRequest)WebRequest.Create(URL);
    Request.ContentType = "application/json";
    Request.Accept = "*/*";
    Request.CookieContainer = Cookies;
    Request.Host = "<HOST HERE>";
    Request.Method = "POST";
    Request.UserAgent = "<USER AGENT HERE>";
    Request.Headers.Add("<HEADER>", "<VALUE>");
    Request.KeepAlive = true;

    byte[] CompressedRequest = SimpleZlib.CompressToBytes(Encoding.UTF8.GetBytes(data), 9);
    Stream RequestStream = Request.GetRequestStream();
    RequestStream.Write(CompressedRequest, 0, CompressedRequest.Length);
    RequestStream.Flush();

    Stream CompressedResponseStream = Request.GetResponse().GetResponseStream();
    byte[] CompressedResponseData = ReadToEnd(CompressedResponseStream);
    string DecompressedResponseData = SimpleZlib.Decompress(CompressedResponseData, null);

    OffersResponse Return = Json.Deserialize<OffersResponse>(DecompressedResponseData);

    int LowestCost = 1000000000;
    Offer BestOffer = new Offer();

    foreach (Offer CurrentOffer in Return.data.offers)
    {
        bool moneyOffer = false;
        int Costs = CurrentOffer.requirementsCost;
        string id = CurrentOffer._id;

        foreach (Requirement CurrentRequirement in CurrentOffer.requirements)
        {
            if (CurrentRequirement._tpl == "<TEMPLATE ID HERE>")
            {
                moneyOffer = true;
            }
        }

        if (moneyOffer == false)
        {
            continue;
        }

        if (Costs < LowestCost)
        {
            LowestCost = Costs;
            BestOffer = CurrentOffer;
        }
    }

    BestOfferID = BestOffer._id;
    BestOfferCost = LowestCost;

    string MoneyID = getStack(BestOfferCost);
    while (true)
    {
        BuyRequestAttemptCounter++;

        if (LowestCost > 140000)
        {
            AddLog("No Suitable Item! Skipping! Lowest Item Cost: " + LowestCost.ToString());
            return false;
        }
        else
            AddLog("Best Item Cost: " + LowestCost.ToString() + " | ID: " + BestOfferID);

        int Result = buyOrder(MoneyID);
        if (Result == 0)
        {
            //log info for averaging
            numberPurchased++;
            TotalCost += BestOfferCost;
            averageCost = TotalCost / numberPurchased;

            lblNumPurchased.Text = numberPurchased.ToString();
            lblAverageCost.Text = averageCost.ToString();
            lstPricesPurchased.Items.Add(LowestCost.ToString());

            AddLog("====================================");
            AddLog("Number Purchased: " + numberPurchased);
            AddLog("Average Cost: " + averageCost);
            AddLog("====================================");
            System.Media.SystemSounds.Exclamation.Play();
            return true;
        }
        else if (Result == 1)
            return false;
        else if (Result == 2)
            continue;
        else
            return false;
    }
}
private int buyOrder(string MoneyID)
{
    string data = "<DATA HERE>";
    string URL = "<URL HERE>";

    Cookies.SetCookies(cookie, "PHPSESSID=" + SessionIDTextBox.Text.Replace("PHPSESSID=", ""));
    HttpWebRequest Request = (HttpWebRequest)WebRequest.Create(URL);
    Request.ContentType = "application/json";
    Request.Accept = "*/*";
    Request.CookieContainer = Cookies;
    Request.Host = "<HOST HERE>";
    Request.Method = "POST";
    Request.UserAgent = "<USER AGENT HERE>";
    Request.Headers.Add("<HEADER>", "<VALUE>");
    Request.KeepAlive = true;

    byte[] CompressedRequest = SimpleZlib.CompressToBytes(Encoding.UTF8.GetBytes(data), 9);
    Stream RequestStream = Request.GetRequestStream();
    RequestStream.Write(CompressedRequest, 0, CompressedRequest.Length);
    RequestStream.Flush();

    Stream CompressedResponseStream = Request.GetResponse().GetResponseStream();
    byte[] CompressedResponseData = ReadToEnd(CompressedResponseStream);
    string DecompressedResponseData = SimpleZlib.Decompress(CompressedResponseData, null);

    ResponseRoot Return = Json.Deserialize<ResponseRoot>(DecompressedResponseData);
    string returnErrorCode = DecompressedResponseData.ToString();

    //AddLog(DecompressedResponseData);

    if (Return.err == 0 && returnErrorCode.Contains("id"))
    {
        System.Windows.Forms.Clipboard.SetText(DecompressedResponseData);
        //AddLog("Successful Purchase!");

        return 0;
    }
    else if (returnErrorCode.Contains("1503"))
    {
        //AddLog("Failed with 1503!");
        return 1;
    }
    else if (returnErrorCode.Contains("1512"))
    {
        // AddLog("Failed with 1512!");
        return 2;
    }

    return 3;
}

如上所述,理想情况下,我想同时运行“attachandbuy”和“buyorder”函数,然后最终我将添加一个同时运行的卖出函数。这可能吗?谢谢。

【问题讨论】:

  • 如果 AttachAndBuy()、buy() 和 sell() 之间没有任何依赖关系,您可以为每个函数创建 3 个不同的 bot,而不是在一个 bot 中执行。它将为您在更改方面提供很大的灵活性,并且只部署该功能而不会中断任何其他功能。
  • WebRequest/WebResponse 提供这些方法的异步版本。:GetRequestStreamAsync()GetResponseAsync() 等,以及 [Stream].WriteAsync/ReadAsync 和 Stream/派生类的所有其他异步方法。

标签: c# api asynchronous parallel-processing


【解决方案1】:

您可以通过创建任务列表并执行它们来解决此问题,这会阻止主线程在调用尚未返回时被阻塞,例如-

var tasks = new List<Task>();

for (int i = 0; i < numToBuy; i++)
{
    var task = new Task(() =>
    {
        AttachAndBuy()
    });

    tasks.Add(task);
    task.Start();
}

Task.WaitAll(tasks.ToArray());

(注意:我没有实际测试过这段代码,只是一个粗略的例子)

下面的误会-

您需要为此使用一些并行编程 - https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/task-based-asynchronous-programming?redirectedfrom=MSDN 在您的 for 循环中,例如-

private void RunBot()
{
    numToBuy = (int)nudNumToBuy.Value;

    for (int i = 0; i < numToBuy; i++)
    {
        while (true)
        {
            Parallel.Invoke(() => AttachAndBuy(), () => BuyOrder());
        }
    }
}

【讨论】:

  • 不会因为我如何利用 while(true) 循环而锁定程序吗?另外,我不能从 RunBot 调用 buyOrder,因为它需要从 attachandbuy 传递的参数。
  • 是的,但我认为目标是同时运行这两种方法?从上面-As stated above, ideally i would like to run both the "attachandbuy" and "buyorder" functions at the same time。如有误解请见谅!
  • 不,我认为你是对的,我确实说过,那是我说错了。当我得到我需要仍然有多线程的参数时,我可以在 attachandbuy 函数中调用 Parallel.Invoke 吗?
  • 如果我没看错的话,我认为您正在寻找基于任务的解决方案。您是否希望 for 循环调用 AttachAndBuy,然后在前一个正在执行时继续执行下一个调用?
  • 是的,我们的目标是尽可能快地实现这一目标。目前单线程性能无法让请求以有利可图的方式发生。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多