线程池为具有返回类型的方法 [英] Thread Pooling for a method which has a return type

查看:72
本文介绍了线程池为具有返回类型的方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个名为方法 InitializeCRMService()返回 IOrganizationService 的对象。现在我定义一个名为不同的方法的getConnection(串线)这就要求 InitializeCRMService()基于传递给参数它。如果字符串传递给的getConnection 是单它将启动 IntializeCRMService()方法的单线程实例,但如果传递的字符串是多重的,我需要使用一个线程池,我需要通过方法 QueueUserWorkItem 。该方法 InitializeCRMService 没有输入参数。它只是返回一个服务对象。请找到在的getConnection 方法的代码块如下:

 公共无效的getConnection(串线)
{
ParallelOptions OPS =新ParallelOptions();

如果(thread.Equals(1))
{
的Parallel.For(0,1,I =>
{
动态serviceObject = InitializeCRMService();
});
}
,否则如果(thread.Equals(多))
{
//这里我需要实现多线程使用线程池
//且不平行FOR LOOP ......
// ThreadPool.QueueUserWorkItem(新WaitCallback(InitializeCRMService));
}
}

请注意我的方法 InitializeCRMService ()具有服务对象的返回类型。



请告诉我,我该如何实现它。


< DIV CLASS =h2_lin>解决方案

由于要在线程池来执行InitializeCRMService时,可用插槽,而你只有一次执行此,该解决方案取决于你想用做什么InitializeCRMService的返回值。



如果您只想忽略它,我有两个选择为止。






选项1



 公共无效的getConnection(串线)
{
//我发现OPS不被使用
// ParallelOptions OPS =新ParallelOptions();
如果(thread.Equals(1))
{
的Parallel.For(0,1,I =>
{
//你不'吨真的需要有一个变量
/ *动态serviceObject = * / InitializeCRMService();
});
}
,否则如果(thread.Equals(多))
{
ThreadPool.QueueUserWorkItem

新WaitCallback

(_)=>
{
//你并不真的需要有一个变量
/ *动态serviceObject = * / InitializeCRMService();
}

);
}
}

在另一方面,如果你需要将它传递地方来存储它的重复使用以后,你可以做到这一点是这样的:

 公共无效的getConnection(串线)
{
//我发现OPS不被使用
// ParallelOptions OPS =新ParallelOptions();

如果(thread.Equals(1))
{
的Parallel.For(0,1,I =>
{
/ /在我看来,一个好主意,在这里走上相同的道路太
//动态serviceObject = InitializeCRMService();
店(InitializeCRMService());
});
}
,否则如果(thread.Equals(多))
{
ThreadPool.QueueUserWorkItem

新WaitCallback

(_)=>
{
店(InitializeCRMService());
}

);
}
}






凡商店将是这样的:

 私人无效店(动态serviceObject)
{
//店内serviceObject什么地方可以用到它。
//根据你的情况,你可能想
//设置标志或使用的ManualResetEvent通知
//这serviceObject准备就绪,可以使用。
//任何预proccess可以在这里也做。
//保重线程关联,
//的,因为这可能来自线程池
//和消费线程可能是另一个,
//你可能需要一些同步。
}

现在,如果你需要让你的类的客户端访问serviceObject,你可以采取以下方式:

  //注:我将其标记为局部的,因为这里可能有$ b没有表现出其他的代码$ b //尤其我不会再写getConnection方法。这就是说... 
//你可以拥有一切在一个单一的文件一个单独的块,而无需使用的部分。
公共部分类YourClass
{
专用动态_serviceObject;

私人无效店(动态serviceObject)
{
_serviceObject = serviceObject;
}

公共动态ServiceObject
{
得到
{
返回_serviceObject;
}
}
}



但是,这并不需要关心的所有案件。特别是,如果你想拥有的线程等待serviceObject做好准备:

 公共部分类YourClass 
{
私人的ManualResetEvent _serviceObjectWaitHandle =新的ManualResetEvent(假);
专用动态_serviceObject;


私人无效店(动态serviceObject)
{
_serviceObject = serviceObject;
//如果你需要尽快做一些工作作为_serviceObject准备...
//那么就可以在这里完成,这仍可能是线程池线程。
//如果你需要调用类似的用户界面......
//你需要使用的BeginInvoke或类似的解决方案。
_serviceObjectWaitHandle.Set();
}

公共无效WaitForServiceObject()
{
//你也可能会使其他重载,只是为了方便。
//这将等到商店执行
//当_serviceObjectWaitHandle.Set()被调用
//这会让其他线程通过。
_serviceObjectWaitHandle.WaitOne();
}

公共动态ServiceObject
{
得到
{
返回_serviceObject;
}
}
}






不过,我还没有涵盖所有的场景。对于intance ......如果getConnection时多次调用会发生什么?我们需要决定,如果我们要允许这一点,如果我们这样做,我们做什么与老serviceObject? (我们需要调用一些解雇吗?)。这可能会产生问题,如果我们允许多个线程调用的getConnection一次。所以,在默认情况下,我会说,我们不这样做,但是我们并不想阻止其他线程要么...



该如何解决?具体情况如下:

  //这是同一类
的另一部分//这其中包括的getConnection
公共部分类YourClass
{
// 1,如果的getConnection被调用,否则为0
私人诠释_initializingServiceObject;

公共无效的getConnection(串线)
{
如果(Interlocked.CompareExchange(REF _initializingServiceObject,1,0)== 0)
{
//来吧,这是第一次的getConnection称为

//我发现,OPS不使用
// ParallelOptions OPS =新ParallelOptions();
如果(thread.Equals(1))
{
的Parallel.For(0,1,I =>
{
//这似乎我一个好主意,在这里走上相同的道路太
//动态serviceObject = InitializeCRMService();
店(InitializeCRMService());
});
}
,否则如果(thread.Equals(多))
{
ThreadPool.QueueUserWorkItem

新WaitCallback

(_)=>
{
店(InitializeCRMService());
}

);
}
}
}
}






最后,如果我们允许多个线程使用_serviceObject,而_serviceObject不是线程安全的,我们可以遇到麻烦。使用监视器或使用读写锁两个备选方案来解决这一点。



你还记得吗?



 公共动态ServiceObject 
{
得到
{
返回_serviceObject;
}
}



好吧,你想有来电接入_serviceObject当它在一个背景下,将防止他人线程进入(见System.Threading.Monitor),并确保其停止使用,然后离开这方面,我前面提到的。



现在考虑调用程序线程仍可以存储_serviceObject副本的地方,然后离开syncrhonization,然后做_serviceObject东西,而当另一个线程正在使用它,可能会发生。



我曾经以为每一个角落的情况下,当涉及到线程。但是,如果你有超过调用线程控制,可以只用上面的财产做表现非常好。如果你不...让我们谈论它,我警告你,也可以是广泛的。






选项2



这是一个完全不同的行为,将给予表彰的 Damien_The_Unbeliever 的在你的问题做出让我觉得你可能打算返回serviceObject。在这种情况下,它不是线程之间共享,并且它是确定在一个时间有多个serviceObject。以及任何需要同步留给来电



好吧,这可能是你一直在寻找的东西:

 公共无效的getConnection(串线,动作<动态>回调)
{
如果(的ReferenceEquals(回调,空))
{
抛出新的ArgumentNullException(回调);
}
//我发现OPS不被使用
// ParallelOptions OPS =新ParallelOptions();
如果(thread.Equals(1))
{
的Parallel.For(0,1,I =>
{
回调(InitializeCRMService() );
});
}
,否则如果(thread.Equals(多))
{
ThreadPool.QueueUserWorkItem

新WaitCallback

(_)=>
{
回调(InitializeCRMService());
} $ b $二)$ b $二);
}
}



应该如何回调看?好了,只要它不是线程之间共享它是确定。为什么?因为每个调用的getConnection线程通过它自己的回调动作,并会收到不同的serviceObject,所以没有风险是什么一个线程确实给它影响什么其他做它(因为它是不一样的serviceObject)。



除非你想有一个线程调用此,然后与其他线程共享它,在这种情况下,它是主叫方的一个问题,它会在另一个地方的解析另一时刻。






最后一件事,你可以使用一个枚举代表你当前传入串线的选项。事实上,由于只有两个选项,你可以考虑使用一个布尔值,除非他们可能会在未来出现更多的情况下。


I have a method named InitializeCRMService() which returns an object of IOrganizationService. Now I am defining a different method named GetConnection(string thread) which calls InitializeCRMService() based on the parameter passed to it. If the string passed to GetConnection is single it will start a single threaded instance of the IntializeCRMService() method, but if the string passed is multiple, I need to use a thread pool where I need to pass the method to QueueUserWorkItem. The method InitializeCRMService has no input parameters. It just returns a service object. Please find below the code block in the GetConnection method:

public void GetConnection(string thread)
{
    ParallelOptions ops = new ParallelOptions();

    if(thread.Equals("one"))
    {
        Parallel.For(0, 1, i =>
        {
            dynamic serviceObject = InitializeCRMService();       
        });
    }
    else if (thread.Equals("multi"))
    {
        // HERE I NEED TO IMPLEMENT MULTITHREADING USING THREAD POOL 
        // AND NOT PARALLEL FOR LOOP......
        // ThreadPool.QueueUserWorkItem(new WaitCallback(InitializeCRMService));
    }
}

Please note my method InitializeCRMService() has a return type of Service Object.

Please tell me how do I implement it.

解决方案

Since you want to execute InitializeCRMService in the ThreadPool when a slot is available, and you are executing this only once, the solution depends on what you want to do with the return value of InitializeCRMService.

If you only want to ignore it, I have two options so far.


Option 1

public void GetConnection(string thread)
{
    //I found that ops is not being used
    //ParallelOptions ops = new ParallelOptions();
    if(thread.Equals("one"))
    {
        Parallel.For(0, 1, i =>
        {
            //You don't really need to have a variable
            /*dynamic serviceObject =*/ InitializeCRMService();
        });
    }
    else if (thread.Equals("multi"))
    {
        ThreadPool.QueueUserWorkItem
        (
             new WaitCallback
             (
                 (_) =>
                 {
                      //You don't really need to have a variable
                      /*dynamic serviceObject =*/ InitializeCRMService();
                 }
             )
        );
    }
}

On the other hand, if you need to pass it somewhere to store it an reuse it later you can do it like this:

public void GetConnection(string thread)
{
    //I found that ops is not being used
    //ParallelOptions ops = new ParallelOptions();

    if(thread.Equals("one"))
    {
        Parallel.For(0, 1, i =>
        {
            //It seems to me a good idea to take the same path here too
            //dynamic serviceObject = InitializeCRMService();
            Store(InitializeCRMService());
        });
    }
    else if (thread.Equals("multi"))
    {
        ThreadPool.QueueUserWorkItem
        (
             new WaitCallback
             (
                 (_) =>
                 {
                      Store(InitializeCRMService());
                 }
             )
        );
    }
}


Where Store would be something like this:

private void Store(dynamic serviceObject)
{
    //store serviceObject somewhere you can use it later.
    //Depending on your situation you may want to
    // set a flag or use a ManualResetEvent to notify
    // that serviceObject is ready to be used.
    //Any pre proccess can be done here too.
    //Take care of thread affinity,
    // since this may come from the ThreadPool
    // and the consuming thread may be another one,
    // you may need some synchronization.
}

Now, if you need to allow clients of your class to access serviceObject, you can take the following approach:

//Note: I marked it as partial because there may be other code not showed here
// in particular I will not write the method GetConnection again. That said...
// you can have it all in a single block in a single file without using partial.
public partial class YourClass
{
    private dynamic _serviceObject;

    private void Store(dynamic serviceObject)
    {
        _serviceObject = serviceObject;
    }

    public dynamic ServiceObject
    {
        get
        {
            return _serviceObject;
        }
    }
}

But this doesn't take care of all the cases. In particular if you want to have thread waiting for serviceObject to be ready:

public partial class YourClass
{
    private ManualResetEvent _serviceObjectWaitHandle = new ManualResetEvent(false);
    private dynamic _serviceObject;


    private void Store(dynamic serviceObject)
    {
        _serviceObject = serviceObject;
        //If you need to do some work as soon as _serviceObject is ready...
        // then it can be done here, this may still be the thread pool thread.
        //If you need to call something like the UI...
        // you will need to use BeginInvoke or a similar solution.
        _serviceObjectWaitHandle.Set();
    }

    public void WaitForServiceObject()
    {
            //You may also expose other overloads, just for convenience.
            //This will wait until Store is executed
            //When _serviceObjectWaitHandle.Set() is called
            // this will let other threads pass.
            _serviceObjectWaitHandle.WaitOne();
    }

    public dynamic ServiceObject
    {
        get
        {
            return _serviceObject;
        }
    }
}


Still, I haven't covered all the scenarios. For intance... what happens if GetConnection is called multiple times? We need to decide if we want to allow that, and if we do, what do we do with the old serviceObject? (do we need to call something to dismiss it?). This can be problematic, if we allow multiple threads to call GetConnection at once. So by default I will say that we don't, but we don't want to block the other threads either...

The solution? Follows:

//This is another part of the same class
//This one includes GetConnection
public partial class YourClass
{
    //1 if GetConnection has been called, 0 otherwise
    private int _initializingServiceObject;

    public void GetConnection(string thread)
    {
        if (Interlocked.CompareExchange(ref _initializingServiceObject, 1, 0) == 0)
        {
            //Go on, it is the first time GetConnection is called

            //I found that ops is not being used
            //ParallelOptions ops = new ParallelOptions();
            if(thread.Equals("one"))
            {
                Parallel.For(0, 1, i =>
                {
                    //It seems to me a good idea to take the same path here too
                    //dynamic serviceObject = InitializeCRMService();
                    Store(InitializeCRMService());
                });
            }
            else if (thread.Equals("multi"))
            {
                ThreadPool.QueueUserWorkItem
                (
                     new WaitCallback
                     (
                         (_) =>
                         {
                              Store(InitializeCRMService());
                         }
                     )
                );
            }
        }
    }
}


Finally, if we are allowing multiple thread to use _serviceObject, and _serviceObject is not thread safe, we can run into trouble. Using monitor or using a read write lock are two alternatives to solve that.

Do you remember this?

    public dynamic ServiceObject
    {
        get
        {
            return _serviceObject;
        }
    }

Ok, you want to have the caller access the _serviceObject when it is in a context that will prevent others thread to enter (see System.Threading.Monitor), and make sure it stop using it, and then leave this context I mentioned before.

Now consider that the caller thread could still store a copy of _serviceObject somewhere, and then leave the syncrhonization, and then do something with _serviceObject, and that may happen when another thread is using it.

I'm used to think of every corner case when it comes to threading. But if you have control over the calling threads, you can do it very well with just the property showed above. If you don't... let's talk about it, I warn you, it can be extensive.


Option 2

This is a totally different behaviour, the commend Damien_The_Unbeliever made in your question made me think that you may have intended to return serviceObject. In that case, it is not shared among threads, and it is ok to have multiple serviceObject at a time. And any synchronization needed is left to the caller.

Ok, this may be what you have been looking for:

public void GetConnection(string thread, Action<dynamic> callback)
{
    if (ReferenceEquals(callback, null))
    {
        throw new ArgumentNullException("callback");
    }
    //I found that ops is not being used
    //ParallelOptions ops = new ParallelOptions();
    if(thread.Equals("one"))
    {
        Parallel.For(0, 1, i =>
        {
            callback(InitializeCRMService());
        });
    }
    else if (thread.Equals("multi"))
    {
        ThreadPool.QueueUserWorkItem
        (
             new WaitCallback
             (
                 (_) =>
                 {
                      callback(InitializeCRMService());
                 }
             )
        );
    }
}

How should the callback look? Well, as soon as it is not shared between threads it is ok. Why? Because each thread that calls GetConnection passes it's own callback Action, and will recieve a different serviceObject, so there is no risk that what one thread does to it affect what the other does to its (since it is not the same serviceObject).

Unless you want to have one thread call this and then shared it with other threads, in which case, it is a problem of the caller and it will be resolved in another place in another moment.


One last thing, you could use an enum to represent the options you currently pass in the string thread. In fact, since there are only two options you may consider using a bool, unless they may appear more cases in the future.

这篇关于线程池为具有返回类型的方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆