2

我有一个名为的方法InitializeCRMService(),它返回一个IOrganizationService. 现在我定义了一个不同的方法,它根据传递给它的参数GetConnection(string thread)调用它。InitializeCRMService()如果传递给的字符串GetConnection是单个的,它将启动该IntializeCRMService()方法的单个线程实例,但如果传递的字符串是多个,我需要使用一个线程池,我需要将方法传递给QueueUserWorkItem. 该方法InitializeCRMService没有输入参数。它只是返回一个服务对象。请在方法中的代码块下方找到GetConnection

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));
    }
}

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

请告诉我如何实现它。

4

1 回答 1

4

由于您希望在插槽可用时在 ThreadPool 中执行 InitializeCRMService,并且您只执行一次,因此解决方案取决于您要如何处理 InitializeCRMService 的返回值。

如果你只想忽略它,到目前为止我有两个选择。


选项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();
                 }
             )
        );
    }
}

另一方面,如果您需要将它传递到某个地方以存储它以供以后重用,您可以这样做:

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());
                 }
             )
        );
    }
}

Store 会是这样的:

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.
}

现在,如果您需要允许您的类的客户端访问 serviceObject,您可以采取以下方法:

//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;
        }
    }
}

但这并不能解决所有情况。特别是如果你想让线程等待 serviceObject 准备好:

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;
        }
    }
}

不过,我还没有涵盖所有场景。例如......如果多次调用 GetConnection 会发生什么?我们需要决定是否允许这样做,如果允许,我们如何处理旧的 serviceObject?(我们需要调用一些东西来解除它吗?)。如果我们允许多个线程同时调用 GetConnection,这可能会出现问题。所以默认情况下我会说我们不这样做,但我们也不想阻塞其他线程......

解决方案?如下:

//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());
                         }
                     )
                );
            }
        }
    }
}

最后,如果我们允许多个线程使用 _serviceObject,而 _serviceObject 不是线程安全的,我们就会遇到麻烦。使用监视器或使用读写锁是解决该问题的两种选择。

你记得这个吗?

    public dynamic ServiceObject
    {
        get
        {
            return _serviceObject;
        }
    }

好的,您希望调用者在 _serviceObject 处于阻止其他线程进入的上下文中时访问它(请参阅 System.Threading.Monitor),并确保它停止使用它,然后离开我之前提到的这个上下文。

现在考虑调用者线程仍然可以在某个地方存储 _serviceObject 的副本,然后离开同步,然后对 _serviceObject 做一些事情,这可能会在另一个线程使用它时发生。

当涉及到线程时,我习惯于考虑每一个角落情况。但是,如果您可以控制调用线程,则只需使用上面显示的属性就可以很好地做到这一点。如果你不...让我们谈谈它,我警告你,它可能是广泛的。


选项 2

这是一种完全不同的行为,Damien_The_Unbeliever在您的问题中提出的赞扬让我认为您可能打算返回 serviceObject。在这种情况下,它不是在线程之间共享的,一次有多个 serviceObject 是可以的。任何需要的同步都留给调用者。

好的,这可能是您一直在寻找的:

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());
                 }
             )
        );
    }
}

回调应该如何看待?好吧,只要它不在线程之间共享就可以了。为什么?因为每个调用GetConnection 的线程都会传递它自己的回调Action,并且会收到不同的serviceObject,所以一个线程对其所做的事情不会影响另一个线程对其所做的事情(因为它不是同一个serviceObject)。

除非你想让一个线程调用它然后与其他线程共享它,在这种情况下,这是调用者的问题,它将在另一个地方解决。


最后一件事,您可以使用枚举来表示您当前在字符串线程中传递的选项。事实上,由于只有两个选项您可以考虑使用 bool,除非它们将来可能会出现更多情况。

于 2012-06-16T04:07:43.507 回答