C# Thread.CurrentThread.CurrentCulture在线程池内的线程中不工作
我有一个将在线程内调用的方法,这些线程由threadpool管理。 该方法正在调用DLL的方法,不幸的是,该方法需要特定的区域设置才能正确执行 在将此方法放在线程池中运行之前,我在应用程序的主线程中运行时以及在手动管理线程时对其进行了测试,它工作正常,但当我将其放在线程池中工作时,未应用区域设置,因此该方法的行为不正确 以下是该方法中受区域设置更改影响(但表现不佳)的部分: 以下是线程池创建结构:C# Thread.CurrentThread.CurrentCulture在线程池内的线程中不工作,c#,multithreading,locale,threadpool,cultureinfo,C#,Multithreading,Locale,Threadpool,Cultureinfo,我有一个将在线程内调用的方法,这些线程由threadpool管理。 该方法正在调用DLL的方法,不幸的是,该方法需要特定的区域设置才能正确执行 在将此方法放在线程池中运行之前,我在应用程序的主线程中运行时以及在手动管理线程时对其进行了测试,它工作正常,但当我将其放在线程池中工作时,未应用区域设置,因此该方法的行为不正确 以下是该方法中受区域设置更改影响(但表现不佳)的部分: 以下是线程池创建结构: foreach (SMSTask smsTask in tasksList) { if (
foreach (SMSTask smsTask in tasksList)
{
if (this.threadsCount < this.threadPoolSize)
{
this.threadsCount++;
ThreadPool.QueueUserWorkItem(new WaitCallback(SendMessage), (object)smsTask);
}
}
在threadpool中运行此方法时,请指导我如何获得正确的结果
更新版本:
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);
}
而这个单独的类别:
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");
}
}
区域性必须应用于线程调用的实际方法中 一种简单的方法应该是将要在线程池上执行的方法包装在另一个方法中,该方法在执行原始方法之前将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
)在SendMessage
方法中,您需要设置线程的当前区域性和UICulture:
var cultureToUse = new System.Globalization.CultureInfo("fa-IR");
var currentThread = System.Threading.Thread.CurrentThread;
currentThread.CurrentCulture = cultureToUse;
currentThread.CurrentUICulture = cultureToUse;
如果当前区域性不是静态的,则必须将其从调用线程传递到排队的工作线程
在这个场景中,如果您控制了SMSTask
类,我将向该类添加一个区域性参数;如果您不控制它,我将添加一个包含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();
}
}
尝试通过
Dispatcher
…或在TaskProcessingThreadFunction
内简单设置Thread.CurrentThread.CurrentCulture
。太奇怪了。令人困惑的是,它确实与线程池如何管理线程的区域设置有关,因为在其他情况下,区域设置应用得很好。这里的问题不就是SendSMS
最有可能在另一个线程上触发某些东西,而该线程不会使用调用线程的相同区域性吗?(除非是内部设置的)。您没有提供该方法的任何实现细节,因此无法确定,但我认为这就是问题所在。这是有争议的,完全取决于该方法需要文化的哪些方面(您并不总是需要同时设置这两个方面)。@James:同意,但因为仅仅设置文化是行不通的,很可能是UIC文化在起作用。此外,很多很多人都不知道UICulture的存在或设置UICulture的必要性。我将代码复制到结构的每个部分,但不幸的是,它没有受到影响。当不设置区域设置时,发送的消息会像???一样到达手机???而不是正确的字符。@Farshid:在这种情况下,我觉得SendSMS方法的行为不正确。这是由您控制的方法还是由第三方控制的方法?是的,它是由第三方控制的,但在不在线程池中时表现良好。WrapCulture
为+1,但最好将Action
更改为WaitCallback
,因为这是threadpool
采用的参数。谢谢。请让我检查一下。@Aidiakapi我很可能错了,但这不只是参数化线程开始吗?:)我的意思是同时作为参数和返回值。OP说他正在使用线程池.QueueUserWorkItem
来管理这些线程。它需要一个WaitCallback
,所以如果你有一个函数WaitCallback-WrapCulture(CultureInfo-ci,WaitCallback-callback)
,它将是普遍适用的。@Aidiakapi啊,是的,我明白你的意思。将很快用您的输入更新答案。
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();
// ToDo: Store current identity in a parameter for 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();
}
}