簡體   English   中英

具有返回類型的方法的線程池

[英]Thread Pooling for a method which has a return type

我有一個名為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()具有服務對象的返回類型。

請告訴我如何實現它。

由於您希望在有可用插槽時在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不同)。

除非您希望有一個線程調用此線程,然后與其他線程共享它,否則,這是調用方的問題,它將在另一時間在另一位置解決。


最后一件事,您可以使用一個枚舉來表示您當前在字符串線程中傳遞的選項。 實際上,由於只有兩種選擇,您可以考慮使用布爾型,除非它們將來可能會出現更多情況。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM