简体   繁体   English

我试图在循环中调用一个方法。它应该在 10 秒内只调用 20 次。 我正在使用如下代码所示的信号量

[英]I am trying to call a method in a loop .It should be called only 20 times in 10 seconds . I am using semaphore like the below code

By using the below code firstly some of the calls are not getting made lets say out of 250 , 238 calls are made and rest doesn't.Secondly I am not sure if the calls are made at the rate of 20 calls per 10 seconds.通过首先使用下面的代码,有些电话没有被拨打,可以说在 250 次中,有 238 次电话拨打,其余的没有。其次,我不确定这些电话是否以每 10 秒 20 次电话的速度进行。

public List<ShowData> GetAllShowAndTheirCast()
    {
        ShowResponse allShows = GetAllShows();

        ShowCasts showCast = new ShowCasts();
        showCast.showCastList = new List<ShowData>();


        using (Semaphore pool = new Semaphore(20, 20))
        {
            for (int i = 0; i < allShows.Shows.Length; i++)
            {
                pool.WaitOne();
                Thread t = new Thread(new ParameterizedThreadStart((taskId) =>
                {
                 showCast.showCastList.Add(MapResponse(allShows.Shows[i]));
                }));
                pool.Release();
                t.Start(i);
            }
        }
        //for (int i = 0; i < allShows.Shows.Length; i++)
        //{
        //    showCast.showCastList.Add(MapResponse(allShows.Shows[i]));
        //}

        return showCast.showCastList;
    }

 public ShowData MapResponse(Show s)
    {
        CastResponse castres = new CastResponse();
        castres.CastlistResponse = (GetShowCast(s.id)).CastlistResponse;
        ShowData sd = new ShowData();
        sd.id = s.id;
        sd.name = s.name;
        if (castres.CastlistResponse != null && castres.CastlistResponse.Any())
        {

            sd.cast = new List<CastData>();
            foreach (var item in castres.CastlistResponse)
            {
                CastData cd = new CastData();
                cd.birthday = item.person.birthday;
                cd.id = item.person.id;
                cd.name = item.person.name;
                sd.cast.Add(cd);
            }

        }
        return sd;
    }
public ShowResponse GetAllShows()
    {
        ShowResponse response = new ShowResponse();
        string showUrl = ClientAPIUtils.apiUrl + "shows";
        response.Shows = JsonConvert.DeserializeObject<Show[]>(ClientAPIUtils.GetDataFromUrl(showUrl));
        return response;
    }

    public CastResponse GetShowCast(int showid)
    {
        CastResponse res = new CastResponse();
        string castUrl = ClientAPIUtils.apiUrl + "shows/" + showid + "/cast";
        res.CastlistResponse = JsonConvert.DeserializeObject<List<Cast>>(ClientAPIUtils.GetDataFromUrl(castUrl));
        return res;
    }

All the Calls should be made , but I am not sure where they are getting aborted and even please let me know how to check the rate of calls being made.应该拨打所有电话,但我不确定他们在哪里中止,甚至请让我知道如何检查拨打的电话率。

I'm assuming that your goal is to process all data about shows but no more than 20 at once.我假设您的目标是处理有关节目的所有数据,但一次不超过 20 个。 For that kind of task you should probably use ThreadPool and limit maximum number of concurrent threads using SetMaxThreads .对于这种任务,您可能应该使用ThreadPool并使用SetMaxThreads限制最大并发线程SetMaxThreads

https://docs.microsoft.com/en-us/dotnet/api/system.threading.threadpool?view=netframework-4.7.2 https://docs.microsoft.com/en-us/dotnet/api/system.threading.threadpool?view=netframework-4.7.2

You have to make sure that collection that you are using to store your results is thread-safe.您必须确保用于存储结果的集合是线程安全的。

showCast.showCastList = new List<ShowData>();

I don't think that standard List is thread-safe.我不认为标准 List 是线程安全的。 Thread-safe collection is ConcurrentBag (there are others as well).线程安全的集合是 ConcurrentBag(还有其他的)。 You can make standard list thread-safe but it requires more code.您可以使标准列表线程安全,但它需要更多代码。 After you are done processing and need to have results in list or array you can convert collection to desired type.完成处理并需要在列表或数组中获得结果后,您可以将集合转换为所需的类型。

https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentbag-1?view=netframework-4.7.2 https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentbag-1?view=netframework-4.7.2

Now to usage of semaphore.现在来使用信号量。 What your semaphore is doing is ensuring that maximum 20 threads can be created at once.您的信号量正在做的是确保一次最多可以创建 20 个线程。 Assuming that this loop runs in your app main thread your semaphore has no purpose.假设此循环在您的应用程序主线程中运行,则您的信号量没有任何用途。 To make it work you need to release semaphore once thread is completed;要使其工作,您需要在线程完成后释放信号量; but you are calling thread Start() after calling Release() .但是您在调用Release()之后调用线程Start() Release() That results in thread being executed outside "critical area".这导致线程在“关键区域”之外执行。

using (Semaphore pool = new Semaphore(20, 20)) {
    for (int i = 0; i < allShows.Shows.Length; i++) {
        pool.WaitOne();
        Thread t = new Thread(new ParameterizedThreadStart((taskId) =>
        {
            showCast.showCastList.Add(MapResponse(allShows.Shows[i]));
            pool.Release();
        }));            
        t.Start(i);
    }
}

I did not test this solution;我没有测试这个解决方案; additional problems might arise.可能会出现其他问题。

Another issue with this program is that it does not wait for all threads to complete.该程序的另一个问题是它不会等待所有线程完成。 Once all threads are started;一旦所有线程启动; program will end.程序将结束。 It is possible (and in your case I'm sure) that not all threads completed its operation;有可能(在您的情况下我敢肯定)并非所有线程都完成了操作; this is why ~240 data packets are done when program finishes.这就是为什么在程序完成时会完成大约 240 个数据包的原因。

thread.Join();

But if called right after Start() it will stop main thread until it is completed so to keep program concurrent you need to create a list of threads and Join() them at the end of program.但是如果在Start()之后立即调用,它将停止主线程直到它完成,因此为了保持程序并发,您需要在程序结束时创建一个线程列表和Join()它们。 It is not the best solution.这不是最好的解决方案。 How to wait on all threads that program can add to ThreadPool如何等待程序可以添加到ThreadPool所有线程

Wait until all threads finished their work in ThreadPool 等到所有线程在 ThreadPool 中完成工作

As final note you cannot access loop counter like that.最后要注意的是,您不能像那样访问循环计数器。 Final value of loop counter is evaluated later and with test I ran;稍后评估循环计数器的最终值,并使用我运行的测试; code has tendency to process odd records twice and skip even.代码倾向于处理奇数记录两次并跳过偶数记录。 This is happening because loop increases counter before previous thread is executed and causes to access elements outside bounds of array.发生这种情况是因为循环在执行前一个线程之前增加了计数器并导致访问数组边界之外的元素。

Possible solution to that is to create method that will create thread.可能的解决方案是创建将创建线程的方法。 Having it in separate method will evaluate allShows.Shows[i] to show before next loop pass.将它放在单独的方法中将评估allShows.Shows[i]以在下一次循环传递之前show

public void CreateAndStartThread(Show show, Semaphore pool, ShowCasts showCast)
{
        pool.WaitOne();
        Thread t = new Thread(new ParameterizedThreadStart((s) => {
            showCast.showCastList.Add(MapResponse((Show)s));
            pool.Release();
        }));
    t.Start(show);
}

Concurrent programming is tricky and I would highly recommend to do some exercises with examples on common pitfalls.并发编程很棘手,我强烈建议做一些关于常见陷阱的例子的练习。 Books on C# programming are sure to have a chapter or two on the topic.关于 C# 编程的书籍肯定有一两章关于这个主题。 There are plenty of online courses and tutorials on this topic to learn from.有很多关于这个主题的在线课程和教程可供学习。

Edit: Working solution.编辑:工作解决方案。 Still might have some issues.可能还是有些问题。

    public ShowCasts GetAllShowAndTheirCast()
        {
            ShowResponse allShows = GetAllShows();

            ConcurrentBag<ShowData> result = new ConcurrentBag<ShowData>();
            using (var countdownEvent = new CountdownEvent(allShows.Shows.Length))
            {
                using (Semaphore pool = new Semaphore(20, 20))
                {
                    for (int i = 0; i < allShows.Shows.Length; i++)
                    {
                        CreateAndStartThread(allShows.Shows[i], pool, result, countdownEvent);
                    }

                    countdownEvent.Wait();
                }     
            }
            return new ShowCasts() { showCastList = result.ToList() };
        }

    public void CreateAndStartThread(Show show, Semaphore pool, ConcurrentBag<ShowData> result, CountdownEvent countdownEvent)
        {
            pool.WaitOne();
            Thread t = new Thread(new ParameterizedThreadStart((s) =>
            {
                result.Add(MapResponse((Show)s));
                pool.Release();
                countdownEvent.Signal();
            }));

            t.Start(show);
        }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 我使用信号量错了吗? - Am i using semaphore wrong? 我正在尝试使用此代码 sHdrMetadataFormatCurrentlySupported(HdrMetadataFormat.HDR10); - I am trying to use this code sHdrMetadataFormatCurrentlySupported(HdrMetadataFormat.HDR10); 为什么我在尝试调用 ShowStatement 方法时会收到 System.InvalidCastException? - Why am I receiving an System.InvalidCastException when I am trying to call my ShowStatement method? 我正在尝试导出excel并使其受密码保护。 我的代码如下。但我收到错误 - I am trying to export an excel and make it password protected. My code is given below.But i am getting error 我正在尝试使用带有IronPython nuget的C#调用python - I am trying to call python using C# with the IronPython nuget 不确定我是否正确使用多态,如果我可以,我只能调用父级中的方法 - Not sure if I am using polymorphism correctly and if I am can I only call methods that are in the parent 我正在尝试将记录插入到对象列表中,但在调用该方法时没有插入任何内容。 我究竟做错了什么? - I am trying to insert records into a an object list but nothing gets inserted when I call the method. What am I doing wrong? 我正在尝试使用 c# 从 xml 文件中读取 url - I am trying to read a url from an xml file using c# using below 我试图在一个类中创建一个方法,并试图在单击按钮时从另一个类(窗体)调用 - I am trying to create a method in one class and trying to call from another class (form) on click of a button 我正在尝试将四叉树代码更新为八叉树 - I am trying to update a Quadtree code to an Octree
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM