Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Thread.CurrentThread.CurrentCulture not working in a thread inside a threadpool

I have a method which will be called inside a thread and those threads are managed by threadpool. The method is calling a DLL's method which unfortunately requires a specific locale for being performed correctly.

Before puting this method to be ran by threadpool, I've tested it while running in application's main thread and also while I manually manage the threads and it works fine, but when I put it into work inside a threadpool, locale is not applied and consequently the method does not behave correctly.

Here is the part of the method which should be affected by locale change (but not behave well):

CultureInfo before = Thread.CurrentThread.CurrentCulture;

Thread.CurrentThread.CurrentCulture = new CultureInfo("fa-IR");
int result = tsms.SendSMS(smsTask.MobileNumber.MobileNumberInString, smsTask.Message);
Thread.CurrentThread.CurrentUICulture = before;

and here is the threadpool creating structure:

foreach (SMSTask smsTask in tasksList)
{
    if (this.threadsCount < this.threadPoolSize)
    {
        this.threadsCount++;                        
        ThreadPool.QueueUserWorkItem(new WaitCallback(SendMessage), (object)smsTask);
    }
}

I also tried setting locale to thread object like below but it didn't solve the problem: (line 2 and 3):

threadObject = new Thread(new ThreadStart(TaskProcessingThreadFunction));
threadObject.CurrentCulture = new CultureInfo("fa-IR");
threadObject.CurrentUICulture = new CultureInfo("fa-IR");
threadObject.Start();

Please guide me on how should I get the correct result while this method is being ran inside threadpool.

Updated Version:

this.isTerminated = false;
            Thread.Sleep(1000);
            while (!this.isTerminated)
            {
                Thread.Sleep(1000);
                IList<SMSTask> tasksList = dataProvider.GetTasks(this.minimumRetryTimeInSeconds);

                if (tasksList == null || tasksList.Count < 1)
                    continue;
                singleTaskConsoleObject(" " + tasksList.Count + " task(s) fetched for sending.");

                var cultureToUse = new System.Globalization.CultureInfo("fa-IR");
                var currentThread = System.Threading.Thread.CurrentThread;
                currentThread.CurrentCulture = cultureToUse;
                currentThread.CurrentUICulture = cultureToUse;

                var currentIdentity = System.Security.Principal.WindowsIdentity.GetCurrent();
                foreach (SMSTask smsTask in tasksList)
                {
                    if (this.threadsCount < this.threadPoolSize)
                    {
                        this.threadsCount++;
                        smsTask.Iden = currentIdentity;
                        ThreadPool.QueueUserWorkItem(new WaitCallback(SendMessage), (object)smsTask);
                    }
                }
                while (this.threadsCount > 0)
                    Thread.Sleep(50);
            }

Other Methods:

private void SendMessage(object smsTask)
        {
            System.Security.Principal.WindowsImpersonationContext impersonationContext = null;
            try
            {
                SMSTask smsTaskEntity = (SMSTask)smsTask;
                impersonationContext = ((System.Security.Principal.WindowsIdentity)(smsTaskEntity.Iden)).Impersonate();

                SmsSender smsSender = new SmsSender();
                SMSSendingResponse response = smsSender.SendSMS(smsTaskEntity);
                bool loggingResult = dataProvider.UpdateResponse(response);
                singleTaskGridObject(response, loggingResult);

                this.threadsCount--;
            }
            finally
            {
                if (impersonationContext != null)
                {
                    impersonationContext.Undo();
                }
            }
        }

And this separate class:

public SMSSendingResponse SendSMS(SMSTask smsTask)
{

        TSMSLIB_TLB.TSMS_Tooba tsms = new TSMS_Tooba();

        SendingResult sendingResult = SendingResult.Initial_Condition;
        try
        {

            System.Globalization.CultureInfo before = System.Threading.Thread.CurrentThread.CurrentCulture;

            System.Threading.Thread.CurrentThread.CurrentCulture =
                new System.Globalization.CultureInfo("fa-IR");

            string msg = string.Format(System.Threading.Thread.CurrentThread.CurrentCulture, smsTask.Message);

            int result = tsms.SendSMS(smsTask.MobileNumber.MobileNumberInString, msg);
            System.Threading.Thread.CurrentThread.CurrentUICulture = before;
            if (result > 0)
            {                    
                return new SMSSendingResponse(smsTask, SMSDeclarations.SendingStatus.SentSuccessfully, result.ToString());
            }
            else
            {
                foreach (SendingResult sResult in Enum.GetValues(typeof(SendingResult)))
                {
                    if (result == (int)sResult)
                    {
                        sendingResult = sResult;
                    }
                }
                return new SMSSendingResponse(smsTask, SMSDeclarations.SendingStatus.SendingFailed,
                    result.ToString(), sendingResult.ToString());
            }
        }
        catch (Exception ex)
        {
            return new SMSSendingResponse(smsTask, SMSDeclarations.SendingStatus.SendingFailed,
                    "0".ToString(), "Exception occured");
        }

    }
like image 653
Farshid Avatar asked Jan 01 '14 18:01

Farshid


1 Answers

The culture must be applied inside the actual method called by the thread.

A simple way should be to wrap the method to be executed on the thread pool inside another method that applies the CultureInfo from the thread enqueuing the job to the thread pool thread before executing the original method, something like;

private static WaitCallback PropagateCulture(WaitCallback action)
{
    var currentCulture   = Thread.CurrentThread.CurrentCulture;
    var currentUiCulture = Thread.CurrentThread.CurrentUICulture;
    return (x) =>
    {
        Thread.CurrentThread.CurrentCulture   = currentCulture;
        Thread.CurrentThread.CurrentUICulture = currentUiCulture;
        action(x);
    };
}

Given that method, you just submit to the threadpool using;

ThreadPool.QueueUserWorkItem(PropagateCulture(SendMessage), (object)smsTask);

(thanks to Aidiakapi for pointing out WaitCallback in the comments below)

like image 128
Joachim Isaksson Avatar answered Oct 06 '22 09:10

Joachim Isaksson