Thread.CurrentThread.CurrentCulture不在线程池内的线程中工作

时间:2014-01-01 18:51:11

标签: c# multithreading locale threadpool cultureinfo

我有一个将在线程内调用的方法,这些线程由线程池管理。 该方法调用DLL的方法,遗憾的是,该方法需要特定的语言环境才能正确执行。

在通过threadpool运行此方法之前,我已经在应用程序的主线程中运行时进行了测试,同时我手动管理线程并且工作正常,但是当我将它放入线程池中时,locale是未应用,因此该方法行为不正确。

以下是应该受区域设置更改影响的方法部分(但行为不正常):

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;

这里是线程池创建结构:

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

我也尝试过将locale设置为如下所示的线程对象,但它没有解决问题: (第2和第3行):

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

请指导我在线程池中运行此方法时如何获得正确的结果。

更新版本:

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

其他方法:

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

这个单独的课程:

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 个答案:

答案 0 :(得分:2)

必须在线程调用的实际方法中应用文化。

一个简单的方法应该是在线程池中包含要执行的方法,该方法在执行原始方法之前将从排队作业的线程中的CultureInfo应用到线程池线程,类似于;

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

鉴于该方法,您只需使用;

提交给线程池
ThreadPool.QueueUserWorkItem(PropagateCulture(SendMessage), (object)smsTask);

(感谢Aidiakapi在下面的评论中指出WaitCallback

答案 1 :(得分:0)

SendMessage方法中,您需要设置线程的当前文化和它的UICulture:

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

如果当前文化不是静态的,那么必须将它从调用线程传递给排队的工作线程。

在这种情况下,如果您控制它,我会向SMSTask类添加一个culture参数,如果没有,我会添加一个包含SMSTask和文化的包装类,如果你不控制它并使用新类作为SendMessage方法的参数。

<强>更新

这是另一个想法:如果SendMessage在主线程上下文中运行时正常工作,但更改文化不会影响在线程池中运行时的结果,问题可能是该方法从当前用户中选择信息

您可以在排队线程池请求并将其作为参数传递给SendMessage方法之前保存当前标识来测试此理论:

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

SendMessage方法中,您可以检索调用线程的标识并模拟该用户,执行您的工作,然后撤消模拟:

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