Thread.CurrentThread.CurrentCulture not working in

2019-06-19 01:12发布

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

    }

2条回答
成全新的幸福
2楼-- · 2019-06-19 01:28

Within your SendMessage method, you need to set both the thread's current culture and it's UICulture:

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

If the current culture is not static, then you must pass it from the calling thread to the queued worker thread.

In this scenario, I would add a culture parameter to the SMSTask class if you control it or, if not, add a wrapper class that contains both the SMSTask and the culture to use if you do not control it and use the new class as the parameter to the SendMessage method.

Update

Here is another thought: if SendMessage works correctly while running in the main thread context, but changing the culture does not affect the results when running in a thread pool, the issue may be that the method picks information up from the current user.

You can test this theory by saving the current identity before queueing the threadpool request and passing as a parameter to your SendMessage method:

var currentIdentity = System.Security.Principal.WindowsIdentity.GetCurrent();
// ToDo: Store current identity in a parameter for SendMessage

Within the SendMessage method, you can retrieve the identity of the calling thread and impersonate that user, perform your work, then undo the impersonation:

    System.Security.Principal.WindowsImpersonationContext impersonationContext = null;
    try {
        // Get the current identity from the SendMessage parameter and use it to impersonate that user on this thread
        impersonationContext = currentIdentity.Impersonate();

        // Do work

    } finally {
        // Undo the impersonation
        if (impersonationContext != null) {
            impersonationContext.Undo();
        }
    }
查看更多
The star\"
3楼-- · 2019-06-19 01:44

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)

查看更多
登录 后发表回答