[并发并行]_[C/C++]_[使用线程本地存储Thread Local Storage(TLS)-win32和pthread比较]

场景:

1.  需要统计某个线程的对象上创建的个数.

2. 当创建的堆空间需要根据线程需要创建和结束时销毁时.

3. 因为范围是线程只能看到自己的存储数据,所以不需要临界区或互斥量来维护自己的堆内存. 加入如果用全局std::map实现,那么必须在put和get时加锁,这是很损耗资源的.

4. 可以用在维护一个连接,比如socket,database连接.

说明:

1. Java也有自己的线程本地存储ThreadLocal

2. pthread的win32版本: http://sourceware.org/pthreads-win32/

例子test_TSL.cpp:

#include "stdafx.h"
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <assert.h>
#include <windows.h>
#include "pthread.h"

#define THREADCOUNT 4
DWORD dwTlsIndex;
VOID ErrorExit(const char*); 

static pthread_barrier_t barrier  = NULL;
static pthread_once_t random_is_initialized = PTHREAD_ONCE_INIT;
static pthread_key_t key;

VOID CommonFunc(VOID)
{
   LPVOID lpvData; 

// Retrieve a data pointer for the current thread. 

   lpvData = TlsGetValue(dwTlsIndex);
   if ((lpvData == 0) && (GetLastError() != ERROR_SUCCESS))
      ErrorExit("TlsGetValue error"); 

// Use the data stored for the current thread.
   int64_t* value = (int64_t*)lpvData;
   printf("common: thread %d: lpvData=%lx : value=%lld\n",
      GetCurrentThreadId(), lpvData,*value); 

   Sleep(5000);
} 

DWORD WINAPI ThreadFunc(VOID)
{
   LPVOID lpvData; 

// Initialize the TLS index for this thread. 

   lpvData = (LPVOID) LocalAlloc(LPTR, 8);
   if (! TlsSetValue(dwTlsIndex, lpvData))
      ErrorExit("TlsSetValue error"); 

   int64_t* value = (int64_t*)lpvData;
   *value = GetCurrentThreadId();
   printf("thread %d: lpvData=%lx\n", GetCurrentThreadId(), lpvData); 

   CommonFunc(); 

// Release the dynamic memory before the thread returns. 

   lpvData = TlsGetValue(dwTlsIndex);
   if (lpvData != 0)
      LocalFree((HLOCAL) lpvData); 

   return 0;
}

void TestWin32TLS()
{
   DWORD IDThread;
   HANDLE hThread[THREADCOUNT];
   int i; 

// Allocate a TLS index. 

   if ((dwTlsIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES)
      ErrorExit("TlsAlloc failed");

   //1. 注意,这个dwTlsIndex 并不是从0开始.
   printf("dwTlsIndex %ld\n",dwTlsIndex);
// Create multiple threads. 

   for (i = 0; i < THREADCOUNT; i++)
   {
      hThread[i] = CreateThread(NULL, // default security attributes
         0,                           // use default stack size
         (LPTHREAD_START_ROUTINE) ThreadFunc, // thread function
         NULL,                    // no thread function argument
         0,                       // use default creation flags
         &IDThread);              // returns thread identifier 

   // Check the return value for success.
      if (hThread[i] == NULL)
         ErrorExit("CreateThread error\n");
   } 

   for (i = 0; i < THREADCOUNT; i++)
      WaitForSingleObject(hThread[i], INFINITE); 

   TlsFree(dwTlsIndex);
}

void CommonFuncPthread(void)
{
   void* lpvData = pthread_getspecific(key);
   int64_t* value = (int64_t*)lpvData;
   printf("common: thread %d: lpvData=%lx : value=%lld\n",
      GetCurrentThreadId(), lpvData,*value);
}

void* StartPthread(void* data)
{
	int64_t *buf = (int64_t*)malloc(256);
	*buf = GetCurrentThreadId();
	pthread_setspecific(key, buf);
	CommonFuncPthread();
	free(buf);
    pthread_barrier_wait(&barrier);

	return NULL;
}

//1.只调用一次,可以放在随意一个work thread执行函数里都可以,会随机找一个线程执行.
//2.类似win32的DLL_PROCESS_ATTACH只执行一次.

void TestPthreadTLS()
{
	pthread_key_create(&key, NULL);
	pthread_barrier_init(&barrier,NULL, THREADCOUNT + 1);
	for(int i = 0; i< THREADCOUNT; ++i)
	{
		pthread_t t;
		pthread_create(&t,NULL,StartPthread,NULL);
		pthread_detach(t);
	}
	//1.等待其他线程执行完.
	pthread_barrier_wait(&barrier);
	pthread_key_delete(key);
}

DWORD main(VOID)
{
   //1.win32TLS
   printf("TestWin32TLS\n");
   TestWin32TLS();
   //1.pthread TLS
   printf("TestPthreadTLS\n");
   TestPthreadTLS();
   return 0;
} 

VOID ErrorExit (const char* lpszMessage)
{
   fprintf(stderr, "%s\n", lpszMessage);
   ExitProcess(0);
}

.

输出:

TestWin32TLS
dwTlsIndex 26
thread 8452: lpvData=714f50
common: thread 8452: lpvData=714f50 : value=8452
thread 8460: lpvData=7153d0
common: thread 8460: lpvData=7153d0 : value=8460
thread 8456: lpvData=715610
common: thread 8456: lpvData=715610 : value=8456
thread 8464: lpvData=715190
common: thread 8464: lpvData=715190 : value=8464
TestPthreadTLS
common: thread 8520: lpvData=3b4eb0 : value=8520
common: thread 8512: lpvData=3b4ff0 : value=8512
common: thread 8516: lpvData=3b4eb0 : value=8516
common: thread 8524: lpvData=3b4ff0 : value=8524

参考:

https://msdn.microsoft.com/en-us/library/windows/desktop/ms686749(v=vs.85).aspx

https://msdn.microsoft.com/en-us/library/windows/desktop/ms686991(v=vs.85).aspx

http://blog.chinaunix.net/uid-10231348-id-3034751.html

http://blog.csdn.net/liulina603/article/details/17991731

http://linux.die.net/man/3/pthread_once

时间: 2024-08-28 00:26:39

[并发并行]_[C/C++]_[使用线程本地存储Thread Local Storage(TLS)-win32和pthread比较]的相关文章

[并发并行]_[C/C++]_[使用线程本地存储Thread Local Storage(TLS)调用复制文件接口的案例]

使用场景: 1. 在复制文件时,一般都是一个线程调用一个接口复制文件,这时候需要缓存数据,如果每个文件都需要创建独立的缓存,那么内存碎片是很大的. 如果创建一个static的内存区,当多线程调用同一个接口时,多个线程同时使用同一个static缓存会造成数据污染.最好的办法是这个缓存只对这个线程可见, 当线程创建时创建缓存区,当线程结束时销毁缓存区. 2. 代码里有注释: test.cpp #include <iostream> #include "pthread.h" #i

windows TLS (线程本地存储)

windows TLS (线程本地存储) 一.TLS简述和分类 我们知道在一个进程中,所有线程是共享同一个地址空间的.所以,如果一个变量是全局的或者是静态的,那么所有线程访问的是同一份,如果某一个线程对其进行了修改,也就会影响到其他所有的线程.不过我们可能并不希望这样,所以更多的推荐用基于堆栈的自动变量或函数参数来访问数据,因为基于堆栈的变量总是和特定的线程相联系的. 不过如果某些时候(比如可能是特定设计的dll),我们就是需要依赖全局变量或者静态变量,那有没有办法保证在多线程程序中能访问而不互

聊聊Linux中的线程本地存储(1)——什么是TLS

从本篇开始进入另一个话题:线程本地存储(Thread Local Storage),在介绍这个概念前先说说变量和多线程的相关知识. 多线程下的变量模型 在单线程模型下,变量定义有两个维度,那就是在何处定义,以及它的修饰属性(static, extern,auto,register等).extern属性表示声明一个变量 ,与定义无关,在此不作讨论:而register是将变量优化成寄存器里面,不作讨论.与变量定义相关的修饰属性就只有auto和static了.这两个维度可以得到变量的类型,以及它们的行

C# 线程本地存储 调用上下文 逻辑调用上下文

线程本地存储 using System; using System.Threading; using System.Threading.Tasks; namespace ConsoleAppTest { class Program { static void Main(string[] args) { ThreadDataSlotTest.Test(); } } /// <summary> /// 线程本地存储 /// </summary> class ThreadDataSlot

java线程 在其他对象上同步、线程本地存储ThreadLocal:thinking in java4 21.3.6

package org.rui.thread.concurrency; /** * 在其他对象上同步 * synchronized 块必须给定一个在其上进行同步的对象,并且最合理的方式是,使用其方法正在被调用的当前对象 * :synchronized(this), 在 这种方式中,如果获得了synchronized块上的锁, * 那么该对象其他的synchronized方法和临界区就不能被调用了. * 因此,如果在this上同步,临界区的效果就会直接缩小在同步的范围内. * * 有时必须在另一个

线程本地存储及实现原理

本文是<go调度器源代码情景分析>系列 第一章 预备知识的第十小节,也是预备知识的最后一小节. 线程本地存储又叫线程局部存储,其英文为Thread Local Storage,简称TLS,看似一个很高大上的东西,其实就是线程私有的全局变量而已. 有过多线程编程的读者一定知道,普通的全局变量在多线程中是共享的,一个线程对其进行了修改,所有线程都可以看到这个修改,而线程私有的全局变量与普通全局变量不同,线程私有全局变量是线程的私有财产,每个线程都有自己的一份副本,某个线程对其所做的修改只会修改到自

PE线程本地存储

1.静态tls将变量定义在PE文件内部. 使用.tls节存储 .tls节中包含: 初始化数据 用于每个线程初始化和终止的回调函数 TLS索引 2.代码访问tls数据时经过的步骤: (1) 链接时, 链接器设置tls目录中的AddressOfIndex字段. 该字段指向一个位置,该位置保存了程序用到的tls索引 (2) 创建线程时, 将TEB的地址放入fs寄存器来传递tls数组地址. teb+0x2c处字段指向tls数组 (3) 将tls索引值保存到AddressOfIndex字段指向的位置 (4

TLS 线程本地存储

TLS (Thread Local Storage) XP系统上的Portable executable不支持动态加载. https://reverseengineering.stackexchange.com/questions/14171/thread-local-storage-access-on-windows-xp/14186#14186 http://www.cnblogs.com/wuyuan2011woaini/p/6124385.html http://www.cnblogs.

30分钟读懂进程线程、同步异步、阻塞非阻塞、并发并行

基本概念 1 进程和线程 进程(Process): 是Windows系统中的一个基本概念,它包含着一个运行程序所需要的资源.一个正在运行的应用程序在操作系统中被视为一个进程,进程可以包括一个或多个线程.线程是操作系统分配处理器时间的基本单元,在进程中可以有多个线程同时执行代码.进程之间是相对独立的,一个进程无法访问另一个进程的数据(除非利用分布式计算方式),一个进程运行的失败也不会影响其他进程的运行,Windows系统就是利用进程把工作划分为多个独立的区域的.进程可以理解为一个程序的基本边界.是