diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 7b6aac9..95075d2 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -5,12 +5,11 @@
+
+
-
-
-
-
-
+
+
@@ -53,35 +52,35 @@
- {
+ "keyToString": {
+ "ASKED_ADD_EXTERNAL_FILES": "true",
+ "ASKED_MARK_IGNORED_FILES_AS_EXCLUDED": "true",
+ "Python.12.executor": "Run",
+ "Python.123.executor": "Run",
+ "Python.DeviceInfo.executor": "Run",
+ "Python.Main.executor": "Run",
+ "Python.Test.executor": "Run",
+ "Python.test.executor": "Run",
+ "Python.tidevice_entry.executor": "Run",
+ "RunOnceActivity.ShowReadmeOnStart": "true",
+ "RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
+ "RunOnceActivity.git.unshallow": "true",
+ "SHARE_PROJECT_CONFIGURATION_FILES": "true",
+ "git-widget-placeholder": "main",
+ "javascript.nodejs.core.library.configured.version": "20.17.0",
+ "javascript.nodejs.core.library.typings.version": "20.17.58",
+ "last_opened_file_path": "C:/Users/zhangkai/Desktop/last-item/Storage",
+ "node.js.detected.package.eslint": "true",
+ "node.js.detected.package.tslint": "true",
+ "node.js.selected.package.eslint": "(autodetect)",
+ "node.js.selected.package.tslint": "(autodetect)",
+ "nodejs_package_manager_path": "npm",
+ "settings.editor.selected.configurable": "org.jetbrains.plugins.gitlab.GitLabSettingsConfigurable",
+ "two.files.diff.last.used.file": "E:/share/iOSAI/Module/FlaskService.py",
+ "vue.rearranger.settings.migration": "true"
}
-}]]>
+}
diff --git a/Module/FlaskService.py b/Module/FlaskService.py
index 1473185..8c607d9 100644
--- a/Module/FlaskService.py
+++ b/Module/FlaskService.py
@@ -242,8 +242,7 @@ def growAccount():
manager = ScriptManager()
event = threading.Event()
# 启动脚本
- thread = threading.Thread(target=manager.growAccount, args=(udid, event))
- thread.start()
+ thread = threading.Thread(target=manager.growAccount, args=(udid, event,))
# 添加到线程管理
code, msg = ThreadManager.add(udid, thread, event)
return ResultData(data="", code=code, message=msg).toJson()
@@ -257,7 +256,6 @@ def watchLiveForGrowth():
manager = ScriptManager()
event = threading.Event()
thread = threading.Thread(target=manager.watchLiveForGrowth, args=(udid, event))
- thread.start()
# 添加到线程管理
ThreadManager.add(udid, thread, event)
return ResultData(data="").toJson()
@@ -300,7 +298,6 @@ def passAnchorData():
event = threading.Event()
# 启动脚本
thread = threading.Thread(target=manager.safe_greetNewFollowers, args=(udid, needReply, event))
- thread.start()
# 添加到线程管理
ThreadManager.add(udid, thread, event)
return ResultData(data="").toJson()
@@ -545,8 +542,8 @@ def delete_last_message():
@app.route("/stopAllTask", methods=['POST'])
def stopAllTask():
idList = request.get_json()
- code, data, msg = ThreadManager.batch_stop(idList)
- return ResultData(code, data, msg).toJson()
+ code, msg = ThreadManager.batch_stop(idList)
+ return ResultData(code, "", msg).toJson()
# @app.route("/killWda", methods=['POST'])
diff --git a/Module/__pycache__/FlaskService.cpython-312.pyc b/Module/__pycache__/FlaskService.cpython-312.pyc
index 4f00336..18b093a 100644
Binary files a/Module/__pycache__/FlaskService.cpython-312.pyc and b/Module/__pycache__/FlaskService.cpython-312.pyc differ
diff --git a/Module/__pycache__/Main.cpython-312.pyc b/Module/__pycache__/Main.cpython-312.pyc
index 077ebd9..284dcb7 100644
Binary files a/Module/__pycache__/Main.cpython-312.pyc and b/Module/__pycache__/Main.cpython-312.pyc differ
diff --git a/Utils/ThreadManager.py b/Utils/ThreadManager.py
index 857b996..845dd47 100644
--- a/Utils/ThreadManager.py
+++ b/Utils/ThreadManager.py
@@ -1,136 +1,73 @@
+import ctypes
import threading
-from concurrent.futures import ThreadPoolExecutor, as_completed
from typing import Dict, Tuple, List
+
from Utils.LogManager import LogManager
+def _async_raise(tid: int, exc_type=KeyboardInterrupt):
+ """向指定线程抛异常,强制跳出"""
+ res = ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), ctypes.py_object(exc_type))
+ if res == 0:
+ raise ValueError("线程不存在")
+ elif res > 1:
+ ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), 0)
+
class ThreadManager:
_tasks: Dict[str, Dict] = {}
_lock = threading.Lock()
@classmethod
def add(cls, udid: str, thread: threading.Thread, event: threading.Event) -> Tuple[int, str]:
- """
- 添加一个线程到线程管理器。
- :param udid: 设备的唯一标识符
- :param thread: 线程对象
- :param event: 用于控制线程退出的 Event 对象
- :return: 状态码和信息
- """
+ LogManager.method_info(f"准备创建任务:{udid}", "task")
with cls._lock:
- if udid in cls._tasks and cls._tasks[udid].get("running", False):
- LogManager.method_info(f"任务添加失败:设备 {udid} 已存在运行中的任务", method="task")
- return 400, f"该设备中已存在任务 {udid}"
+ # 判断当前设备是否有任务
+ if cls._tasks.get(udid, None) is not None:
+ return 1001, "当前设备已存在任务"
+ thread.start()
+ print(thread.ident)
- # 如果任务已经存在但已停止,清理旧任务记录
- if udid in cls._tasks and not cls._tasks[udid].get("running", False):
- LogManager.method_info(f"清理设备 {udid} 的旧任务记录", method="task")
- del cls._tasks[udid]
-
- # 添加新任务记录
cls._tasks[udid] = {
+ "id": thread.ident,
"thread": thread,
- "event": event,
- "running": True
+ "event": event
}
- LogManager.method_info(f"设备 {udid} 开始任务成功", method="task")
- return 200, f"创建任务成功 {udid}"
+ return 200, "创建成功"
@classmethod
def stop(cls, udid: str) -> Tuple[int, str]:
- """
- 停止指定设备的线程。
- :param udid: 设备的唯一标识符
- :return: 状态码和信息
- """
- with cls._lock:
- if udid not in cls._tasks or not cls._tasks[udid].get("running", False):
- LogManager.method_info(f"任务停止失败:设备 {udid} 没有执行相关任务", method="task")
- return 400, f"当前设备没有执行相关任务 {udid}"
-
- task = cls._tasks[udid]
- event = task["event"]
- thread = task["thread"]
-
- LogManager.method_info(f"设备 {udid} 的任务正在停止", method="task")
-
- # 设置停止标志位
- event.set()
-
- # 等待线程结束
- thread.join(timeout=5) # 可设置超时时间,避免阻塞
-
- # 清理任务记录
- del cls._tasks[udid] # 删除任务记录
- LogManager.method_info(f"设备 {udid} 的任务停止成功", method="task")
- return 200, f"当前任务停止成功 {udid}"
+ try:
+ print(cls._tasks)
+ obj = cls._tasks.get(udid, None)
+ obj["event"].set()
+ r = cls._kill_thread(obj.get("id"))
+ if r:
+ cls._tasks.pop(udid, None)
+ else:
+ print("好像有问题")
+ except Exception as e:
+ print(e)
+ return 200, "操作成功"
@classmethod
- def batch_stop(cls, udids: List[str]) -> Tuple[int, List[str], str]:
- if not udids:
- return 200, [], "无设备需要停止"
-
- fail_list: List[str] = []
-
- # ---------- 1. 瞬间置位 event ----------
- with cls._lock:
- for udid in udids:
- task = cls._tasks.get(udid)
- if task and task.get("running"):
- task["event"].set()
-
- # ---------- 2. 单设备重试 5 次 ----------
- def _wait_and_clean_with_retry(udid: str) -> Tuple[int, List[str], str]:
- """
- 内部重试 5 次,最终返回格式与 batch_stop 完全一致:
- (code, fail_list, msg) fail_list 空表示成功
- """
- for attempt in range(1, 6):
- with cls._lock:
- task = cls._tasks.get(udid)
- if not task:
- return 200, [], "任务已不存在" # 成功
- thread = task["thread"]
-
- thread.join(timeout=5)
- still_alive = thread.is_alive()
-
- # 最后一次重试才清理记录
- if attempt == 5:
- with cls._lock:
- cls._tasks.pop(udid, None)
-
- if not still_alive:
- return 200, [], "已停止"
-
- LogManager.warning(f"[batch_stop] {udid} 第{attempt}/5 次停止未立即结束,重试", udid)
-
- # 5 次都失败
- LogManager.error(f"[batch_stop] {udid} 停止失败(5 次重试后线程仍活)", udid)
- return 1001, [udid], "部分设备停止失败"
-
- # ---------- 3. 并发执行 ----------
- with ThreadPoolExecutor(max_workers=min(32, len(udids))) as executor:
- future_map = {executor.submit(_wait_and_clean_with_retry, udid): udid for udid in udids}
- for future in as_completed(future_map):
- udid = future_map[future]
- code, sub_fail_list, sub_msg = future.result() # ← 现在解包正确
- if code != 200:
- fail_list.extend(sub_fail_list) # 收集失败 udid
-
- # ---------- 4. 返回兼容格式 ----------
- if fail_list:
- return 1001, fail_list, "部分设备停止失败"
- return 200, [], "全部设备停止成功"
+ def batch_stop(cls, ids: List[str]) -> Tuple[int, str]:
+ try:
+ for udid in ids:
+ cls.stop(udid)
+ except Exception as e:
+ print(e)
+ return 200, "停止成功."
@classmethod
- def is_task_running(cls, udid: str) -> bool:
- """
- 检查任务是否正在运行。
- :param udid: 设备的唯一标识符
- :return: True 表示任务正在运行,False 表示没有任务运行
- """
- with cls._lock:
- is_running = cls._tasks.get(udid, {}).get("running", False)
- LogManager.method_info(f"检查设备 {udid} 的任务状态:{'运行中' if is_running else '未运行'}", method="task")
- return is_running
+ def _kill_thread(cls, tid: int) -> bool:
+ """向原生线程 ID 抛 KeyboardInterrupt,强制跳出"""
+ res = ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid),
+ ctypes.py_object(KeyboardInterrupt))
+ if res == 0: # 线程已不存在
+ print("线程不存在")
+ return False
+ if res > 1: # 命中多个线程,重置
+ print("命中了多个线程")
+ ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), 0)
+ print("杀死线程成功")
+ return True
\ No newline at end of file
diff --git a/Utils/__pycache__/LogManager.cpython-312.pyc b/Utils/__pycache__/LogManager.cpython-312.pyc
index e8d0746..1a1e412 100644
Binary files a/Utils/__pycache__/LogManager.cpython-312.pyc and b/Utils/__pycache__/LogManager.cpython-312.pyc differ
diff --git a/Utils/__pycache__/ThreadManager.cpython-312.pyc b/Utils/__pycache__/ThreadManager.cpython-312.pyc
index 3d4d6c7..c24066d 100644
Binary files a/Utils/__pycache__/ThreadManager.cpython-312.pyc and b/Utils/__pycache__/ThreadManager.cpython-312.pyc differ
diff --git a/script/__pycache__/ScriptManager.cpython-312.pyc b/script/__pycache__/ScriptManager.cpython-312.pyc
index 292a2ce..0a0d476 100644
Binary files a/script/__pycache__/ScriptManager.cpython-312.pyc and b/script/__pycache__/ScriptManager.cpython-312.pyc differ