有时对于类里面某些处理函数,我们希望它可以使用多线程技术以加速其执行速度(当我做力扣, 发现提交超时时,我就超想通过多线程的方法用以解决超时的问题,但通常该超还是超,毕竟不知道力扣背后的平台是什么,哈哈哈哈)。对于Python来说,十分的简单。先举一道力扣上的题:

560. 和为K的子数组

给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。

示例 1 :

输入:nums = [1,1,1], k = 2

输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。

说明 :

数组的长度为 [1, 20,000]。

数组中元素的范围是 [-1000, 1000] ,且整数 k 的范围是 [-1e7, 1e7]。

来源:力扣(LeetCode)

链接:https://leetcode-cn.com/problems/subarray-sum-equals-k

著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

以下是我的Python使用多线程的解法

  1. class Solution:
  2. from threading import Lock
  3. start = 0
  4. ret = 0
  5. lock1 = Lock()
  6. lock2 = Lock()
  7. def threadSum(self, nums, k):
  8. while self.start < len(nums):
  9. i = 0
  10. tmp = 0
  11. with self.lock1:
  12. i = self.start
  13. self.start = self.start + 1
  14. while i < len(nums):
  15. tmp = tmp + nums[i]
  16. if tmp == k:
  17. with self.lock2:
  18. self.ret = self.ret + 1
  19. i = i + 1
  20. def subarraySum(self, nums: List[int], k: int) -> int:
  21. from threading import Thread
  22. self.start = 0
  23. self.ret = 0
  24. t1 = Thread(target=self.threadSum, args=(nums, k,))
  25. t2 = Thread(target=self.threadSum, args=(nums, k,))
  26. t3 = Thread(target=self.threadSum, args=(nums, k,))
  27. t4 = Thread(target=self.threadSum, args=(nums, k,))
  28. t1.start()
  29. t2.start()
  30. t3.start()
  31. t4.start()
  32. t1.join()
  33. t2.join()
  34. t3.join()
  35. t4.join()
  36. return self.ret

程序运行没有任何问题,解题的答案也正确,不过毫无疑问,最终提交还是超时了,哈哈哈

但是当我用C++的多线程去解答这道题时,居然编译都没有通过。(多线程什么的,我以后还是用Python了)

  1. #include <iostream>
  2. #include <thread>
  3. #include <mutex>
  4. #include <vector>
  5. using namespace std;
  6. class Solution {
  7. mutex mtx1;
  8. mutex mtx2;
  9. int start;
  10. int ret;
  11. vector<int>threadnums;
  12. void multiSum(int k)
  13. {
  14. while(start < threadnums.size())
  15. {
  16. int i = 0;
  17. mtx1.lock();
  18. i = start;
  19. start++;
  20. mtx1.unlock();
  21. int tmp = 0;
  22. while(i < threadnums.size())
  23. {
  24. tmp += threadnums[i];
  25. if(tmp == k)
  26. {
  27. mtx2.lock();
  28. ret++;
  29. mtx2.unlock();
  30. }
  31. i++;
  32. }
  33. }
  34. }
  35. public:
  36. int subarraySum(vector<int>& nums, int k)
  37. {
  38. start = 0;
  39. ret = 0;
  40. threadnums = nums;
  41. thread t1(multiSum, k);
  42. thread t2(multiSum, k);
  43. thread t3(multiSum, k);
  44. thread t4(multiSum, k);
  45. t1.join();
  46. t2.join();
  47. t3.join();
  48. t4.join();
  49. return ret;
  50. }
  51. };
  52. int main()
  53. {
  54. vector<int>testcase1{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,3,3,3,1,1,1,5};
  55. int k = 5;
  56. Solution *mysolution = new Solution();
  57. cout << "test case result is: " << mysolution->subarraySum(testcase1, k) << endl;
  58. return 0;
  59. }

那么为什么C++会出现这种情况呢?

C++之所以不能直接像Python的代码一样直接使用多线程技术,是因为C++类的成员函数的函数指针不能直接做为参数传到thread当中, 因为C++成员函数指针带有类命名空间,同时成员函数末尾是会被C++编译器加上可以接收对象地址的this指针参数,因此需要将成员函数做一定的转化,将其转化成不被编译器加上this指针,而由我们自己来为该函数维护”this”指针即可,其中一个办法就是在函数之前添加this指针,令类的成员函数变成静态成员函数。需要注意的是,因为静态成员函数不允许访问类中的非静态成员变量(因为没有this指针),所以在线程函数中,在参数中传入this指针。

修改后的代码如下

  1. #include <iostream>
  2. #include <thread>
  3. #include <mutex>
  4. #include <vector>
  5. using namespace std;
  6. class Solution {
  7. mutex mtx1;
  8. mutex mtx2;
  9. int start;
  10. int ret;
  11. vector<int>threadnums;
  12. static void multiSum(void* _this, int k)
  13. {
  14. //this指针是当前对象的首地址,可以通过类型转换来使用
  15. Solution *obj = (Solution *)_this;
  16. while(obj->start < obj->threadnums.size())
  17. {
  18. int i = 0;
  19. obj->mtx1.lock();
  20. i = obj->start;
  21. obj->start++;
  22. obj->mtx1.unlock();
  23. int tmp = 0;
  24. while(i < obj->threadnums.size())
  25. {
  26. tmp += obj->threadnums[i];
  27. if(tmp == k)
  28. {
  29. obj->mtx2.lock();
  30. obj->ret++;
  31. obj->mtx2.unlock();
  32. }
  33. i++;
  34. }
  35. }
  36. }
  37. public:
  38. int subarraySum(vector<int>& nums, int k)
  39. {
  40. start = 0;
  41. ret = 0;
  42. threadnums = nums;
  43. thread t1(multiSum, (void*)this, k);
  44. thread t2(multiSum, (void*)this, k);
  45. thread t3(multiSum, (void*)this, k);
  46. thread t4(multiSum, (void*)this, k);
  47. t1.join();
  48. t2.join();
  49. t3.join();
  50. t4.join();
  51. return ret;
  52. }
  53. };
  54. int main()
  55. {
  56. vector<int>testcase1{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,3,3,3,1,1,1,5};
  57. int k = 5;
  58. Solution *mysolution = new Solution();
  59. cout << "test case result is: " << mysolution->subarraySum(testcase1, k) << endl;
  60. return 0;
  61. }

现在可以编译通过,也是可以得到正确的结果,不过最后的提交依旧还是超时。

所以在C++的类内开发多线程函数时,需要注意以下几点

  • 将线程函数声明为静态函数
  • 创建线程时传递this指针进去(静态成员函数时不存在this指针的)
  • 在线程函数中使用传进来的this指针调用类的成员,特别注意调用类的成员函数时要避免segmantation fault错误。

除了转换成static成员函数的方法外,采用友元函数也是可以的。通过定义线程函数为类的友元函数,令线程函数可以像类自己的成员函数一样访问其成员变量和成员函数。(类的友元函数是定义在类外部,但有权访问类的所有私有成员和保护成员)

参考资料
https://www.cnblogs.com/chay/p/10587490.html